Игра на Godot, която се играе добре с клавиатура и геймпад, обикновено стига до телефоните последна. Тогава преносът се превръща във втора игра: скрипт за джойстик, който мести героя направо, бутони, които сами извикват jump(), урок, който още казва „Натиснете Space“, и телефон, на който бутонът за скок е под изреза на екрана. Тук показваме друг път. Бутоните на екрана натискат същите действия, които натискат клавиатурата и геймпадът, така че кодът на играта не се променя, а подсказките следват това, което играчът държи в ръцете си. Всеки пример на GDScript тук е пуснат в Godot 4.7.1.
#Натискайте действия, не извиквайте кода на играта
Мястото за свързване е Input Map на Godot. Героят ви вече чете Input.get_vector() или Input.is_action_pressed(&"jump"); клавиш, стик и спусък са просто различни събития, вързани към тези действия. Управлението с докосване трябва да е още един източник на същите действия. Input.action_press() приема сила между 0 и 1, така че виртуалният джойстик може да натисне move_right със сила 0,53 точно като истински стик:
extends Node
const DEADZONE := 0.15
func stick_to_actions(raw: Vector2) -> void:
var v := Vector2.ZERO
if raw.length() > DEADZONE:
var t := clampf((raw.length() - DEADZONE) / (1.0 - DEADZONE), 0.0, 1.0)
v = raw.normalized() * t
_set_action(&"move_left", maxf(-v.x, 0.0))
_set_action(&"move_right", maxf(v.x, 0.0))
_set_action(&"move_up", maxf(-v.y, 0.0))
_set_action(&"move_down", maxf(v.y, 0.0))
func _set_action(action: StringName, strength: float) -> void:
if strength > 0.0:
Input.action_press(action, strength)
else:
Input.action_release(action)
raw е отместването на копчето от центъра, разделено на радиуса на джойстика, така че дължината му е от 0 до 1. Извиквайте функцията при всяко плъзгане и веднъж с Vector2.ZERO, когато пръстът се вдигне. Понеже действията се отпускат, щом силата падне до нула, нищо не остава натиснато след пръста.
#Двойната мъртва зона
Ето изненадата, на която попаднахме, когато го пуснахме. Всяко действие има своя мъртва зона, а действията, създадени в кода или в Input Map, започват с 0,2. Input.get_vector() ползва средната мъртва зона на четирите си действия и преоразмерява това, което е над нея. Нашият джойстик вече е махнал своята мъртва зона, така че двете се събират:
- отместване 0,3 дава сила 0,18, а
get_vector()я превръща в нищо; - отместване 0,6 дава 0,53, а
get_vector()връща 0,41; - с
Input.get_vector(&"move_left", &"move_right", &"move_up", &"move_down", 0.0)или с мъртва зона 0 на действията героят получава 0,18 и 0,53.
Играчът го усеща като джойстик, който не прави нищо в първата третина от хода си. Дръжте една мъртва зона, на едно място. Ако искате мъртвата зона на Input Map за истинските стикове, нека джойстикът на екрана праща сурови стойности; ако джойстикът има своя, подайте нулева мъртва зона там, където четете вектора.
#Бутони за няколко пръста
На телефона левият палец държи джойстика, докато десният натиска скок. Button и другите контроли на Godot реагират на емулирани събития от мишката, а мишката е една, така че два пръста върху две контроли си пречат. Събитията от докосване носят index за всеки пръст. Бутон, който помни кой пръст го е натиснал, работи до джойстика:
extends Control
@export var action: StringName = &"jump"
var finger := -1
func _input(event: InputEvent) -> void:
if not event is InputEventScreenTouch:
return
if event.pressed and finger < 0 and get_global_rect().has_point(event.position):
finger = event.index
Input.action_press(action)
get_viewport().set_input_as_handled()
elif not event.pressed and event.index == finger:
release_all()
func release_all() -> void:
if finger >= 0:
finger = -1
Input.action_release(action)
func _notification(what: int) -> void:
if what in [NOTIFICATION_PAUSED, NOTIFICATION_EXIT_TREE, NOTIFICATION_APPLICATION_FOCUS_OUT]:
release_all()
elif what == NOTIFICATION_VISIBILITY_CHANGED and not is_visible_in_tree():
release_all()
При нашето изпълнение вдигането на друг пръст остави скока натиснат, а скриването на бутона или паузата на играта го отпуснаха. Втората част е по-важна, отколкото изглежда. Меню за пауза, което се отваря, докато пръстът е на бутона, или обаждане, което праща приложението на заден план, иначе оставят действието задържано: играчът се връща и героят сам тича към стената.
Godot има и вграден TouchScreenButton. Той е Node2D, а не Control, така че не следва контейнерите и котвите, но натиска действие, следи своя пръст и може да се показва само на сензорни екрани. За един или два бутона в проста подредба е добро начало.
#Разберете какво държи играчът
Управлението с докосване трябва да се вижда само от играчите с докосване, а подсказките трябва да назовават верния клавиш. И за двете трябва устройството, което играчът е ползвал последно. Автоматично зареден възел (autoload), който следи всяко събитие, може да го каже, стига да пренебрегва два вида шум:
extends Node
## Autoload "Device": the kind of device the player used last.
signal device_changed(kind: String)
var kind := "keyboard"
func _input(event: InputEvent) -> void:
var next := kind
if event is InputEventScreenTouch or event is InputEventScreenDrag:
next = "touch"
elif event is InputEventJoypadButton and event.pressed:
next = "gamepad"
elif event is InputEventJoypadMotion and absf(event.axis_value) >= 0.5:
next = "gamepad"
elif event is InputEventKey and event.pressed:
next = "keyboard"
elif event is InputEventMouseButton and event.pressed \
and event.device != InputEvent.DEVICE_ID_EMULATION:
next = "keyboard"
if next != kind:
kind = next
device_changed.emit(kind)
Първият шум е отклонението на стика: износен контролер, оставен на бюрото, праща малки стойности по осите през цялото време и без прага 0,5 подсказките за клавиатура биха прескачали към подсказки за геймпад. При нашето изпълнение отклонение 0,12 не промени нищо. Вторият е собствената емулация на мишка в Godot. „Emulate Mouse From Touch“ е включено по подразбиране, така че всяко докосване пристига и като щракване с мишката с номер на устройството InputEvent.DEVICE_ID_EMULATION. Без тази проверка всяко докосване би върнало играча на телефон към „клавиатура“ и би скрило бутоните, които току-що е докоснал.
За да пробвате управлението на компютър, включете „Emulate Touch From Mouse“ в Project Settings, Input Devices, Pointing. Тогава плъзгането с мишката става плъзгане с пръст. Полезна проверка е, но е само един пръст; случаите с няколко пръста искат истинско устройство или докосвания, изпратени като събития от тест.
#Подсказки според контролера
„Натиснете A за скок“ е вярно на геймпад на Xbox и грешно на геймпад на Nintendo, където долният бутон е с надпис B. Godot отчита бутоните по положение (JOY_BUTTON_A е долният на всеки геймпад), а името на контролера казва семейството:
extends RefCounted
static func family_of(joy_name: String) -> String:
var n := joy_name.to_lower()
for word in ["xbox", "xinput"]:
if n.contains(word):
return "xbox"
for word in ["playstation", "dualsense", "dualshock", "ps4", "ps5"]:
if n.contains(word):
return "playstation"
for word in ["nintendo", "pro controller", "joy-con"]:
if n.contains(word):
return "nintendo"
return "generic"
static func face_label(button: JoyButton, family: String) -> String:
var labels := {
"nintendo": ["B", "A", "Y", "X"],
"playstation": ["south", "east", "west", "north"],
}
return labels.get(family, ["A", "B", "X", "Y"])[button]
Подайте ѝ Input.get_joy_name(event.device). За „Nintendo Switch Pro Controller“ долният бутон излиза B; за „DualSense Wireless Controller“ е „south“ (долу). Символите по бутоните на PlayStation са търговски марки, затова много игри показват тези бутони по положение или с неутрална икона; това означават имената по положение в примера. Непознат геймпад остава с A B X Y, което е отпечатано на повечето контролери на други производители.
На телефона няма клавиш, който да назовете, затова трябва да се смени самият текст: „Плъзнете стика, за да вървите“ вместо „Натиснете WASD, за да вървите“. Пазете отделна версия за докосване на всеки ред от урока, вместо да пъхате знак в нея.
#Изрези и заоблени ъгли
Бутон в долния десен ъгъл на съвременен телефон може да попадне под заоблен ръб или лентата за жестове. DisplayServer.get_display_safe_area() връща частта от екрана, която системата оставя свободна. Сложете управлението в контейнер на целия екран с полета, равни на безопасната зона плюс удобен минимум, и обновявайте полетата при промяна на размера на прозореца, защото завъртането на телефона мести изреза.
#Добавки, които го правят
Събрахме този подход в Pipalo, добавка за Godot 4: фиксиран, плаващ или следващ пръста джойстик, който натиска вашите действия с аналогова сила, бутони за няколко пръста с плъзгане от бутон на бутон, жестове, които пренебрегват докосванията, вече ползвани от бутоните, безопасна зона, следене на устройството със семействата контролери и подсказки, които сменят клавиатура, геймпад и докосване. Тествана е с 19 теста без графика в Godot 4.7.1 на Linux и със симулирани докосвания, още не на истински телефон или с истински геймпади. Самите знаци са в Podkanka, 161 изображения за подсказки за клавиатура, мишка, геймпади и докосване в три стила, с помощник за Godot, който избира знака за действие и устройство. И двете са обявени като „Очаквайте скоро“.
#Често задавани въпроси
Трябва ли джойстикът да мести героя направо?
Не. Натискайте същите действия, които натискат клавиатурата и геймпадът, така че кодът за движение, мъртвата зона и пренастройката на клавишите да са на едно място и версията за докосване да не се разминава с другите.
Защо виртуалният ми джойстик не реагира близо до центъра?
Обикновено защото се събират две мъртви зони: тази на джойстика и 0,2 на действията, която прилага Input.get_vector(). Оставете само едната.
Защо бутоните на екрана се скриват веднага след като ги докосна?
Godot превръща всяко докосване и в емулирано щракване с мишката. Ако проверката ви за устройство приема всяко щракване като „клавиатура“, тя превключва от докосване. Пренебрегвайте събитията, чието устройство е InputEvent.DEVICE_ID_EMULATION.
Мога ли да тествам управлението без телефон?
Отчасти. „Emulate Touch From Mouse“ ви дава един пръст на компютъра; за два пръста пращайте от тест събития InputEventScreenTouch с различни индекси и пробвайте на истинско устройство преди издаването.
Коментари
Коментари
Напишете първия коментар.
Оставете коментар