Godot Engine:格鬥遊戲中的必殺技(大招/絕招/特殊技/Special Move )輸入系統實現

2020-10-25 11:00:43

前言

必殺技系統是格鬥遊戲中必不可少的元素,要觸發角色的必殺技,必須在一個很短的時間內,準確無誤地輸入一個按鍵序列,比如下表中街霸角色隆的部分必殺技:

必殺技按鍵序列
波動拳下,前,拳
升龍拳前,下,前,拳

在上表中還能發現一個隱藏問題,例如:波動拳的下,前,拳 是升龍拳前,下,前,拳的子序列,如果處理不當那麼升龍拳可能永遠不會被觸發。

基本原理

  • 計時器

直接使用作為計時單位,既準確又方便

  • 輸入(事件)佇列

當輸入(事件)佇列和出招表的每一個必殺比較時,如果佇列的長度大於必殺的指令長度,需要刪除佇列頭部的元素以使它們長度相同

  • 呼叫角色必殺技

這個系統只處理使用者輸入,完整實現角色的必殺技還需要結合狀態機,比如使用者輸入了下,前,拳,本系統把使用者想觸發波動拳的訊息交傳送給狀態機,狀態機決定角色是否能發出波動拳。

  • 隱藏問題

上面隱藏問題的處理,實際上就是把處理升龍拳的優先順序提前,實際上就是在字典中把升龍拳放前面:

var combos : Dictionary = { 
	"Shoryuken" : [RIGHT,DOWN,RIGHT,A] ,
	"Hadoken" : [DOWN,RIGHT,A] 
	}

完整的GDScript版必殺技輸入系統

加了註釋

# 格鬥必殺技輸入系統 V3
extends Node

enum { NONE ,UP,DOWN,LEFT,RIGHT,A,B} #和必殺技相關的事件

var action_queue : Array = []#輸入的事件佇列,NONE事件會被過濾掉
var counter := 0#計時(數)器
var is_reading := false#標記是否在輸入必殺技
const MAX_COUNT := 100#最大幀數


### 記錄大招的字典
var combos : Dictionary = { 
	"Shoryuken" : [RIGHT,DOWN,RIGHT,A] ,
	"Hadoken" : [DOWN,RIGHT,A] 
	}


func _process(delta):
	var current_action = read_action()
	if current_action != NONE:
		if not is_reading:
			is_reading = true
		action_queue.push_back(current_action)
		for combo_name in combos:
			if check_combo(action_queue,combos[combo_name]):
				trigger_combo(combo_name)
				print( combo_name )
				reset()
				break
	if is_reading:
		counter += 1
	if counter >= MAX_COUNT:#如果超出了輸入時限。則系統歸零
		reset()

#讀入輸入事件
func read_action():
	var action = NONE
	if Input.is_action_just_pressed("ui_up"):
		action = UP
	if Input.is_action_just_pressed("ui_down"):
		action = DOWN
	if Input.is_action_just_pressed("ui_left"):
		action = LEFT
	if Input.is_action_just_pressed("ui_right"):
		action = RIGHT
	if Input.is_action_just_pressed("ui_j"):
		action = A
	if Input.is_action_just_pressed("ui_k"):
		action = B
	return action


#檢查是否有大招成功被激發
func check_combo(s,t):
	if s.size() < t.size():
		return false
	var c
	if s.size() > t.size():
		c = s.duplicate()
		c.invert()
		c.resize(t.size())
		c.invert()
	else:
		c = s
	for i in range(t.size()):
		if c[i] != t[i]:
			return false
	return true

#觸發角色的大招
func trigger_combo(combo_name):
	get_tree().call_group("Ryu","Combo",combo_name)
	

#系統歸零
func reset():
	action_queue.clear()#清空輸入佇列
	counter = 0#計時器清零
	is_reading = false