Найти в Дзене
Мой путь в геймдев

История создания игры. 5. Разбираемся в игровом движке.

С последней статьи прошло уже приличное количество времени, а прогресса в моём деле не так много как хотелось бы...
Почитал, посмотрел множество информации по Godot и его возможностях, установил на данный момент последнюю доступную версию (4.3 stable) через Steam.
Пока что организовал следующую структуру в себя проекте В папке autoload(автозагрузка) скрипты, которые будут загружаться при старте проекта и доступны из в любом месте для исполнения. В моем случае я пока вынес туда систему сигналов и общий словарь систем доступных в игре и методы работы с этим словарем. signals.gd extends Node signal radioEnabled(currentValue:bool) signal radioOff signal airEnabled(currentValue:bool) signal airOff signal navigationEnabled(currentValue:bool) signal navigationOff signal controlEnabled(currentValue:bool) signal controlOff signal climateEnabled(currentValue:bool) signal climateOff systems.gd extends Node var system:Dictionary = {
'radioEnabled': true,
'airEnabled': true,
'navigationEnabled'

С последней статьи прошло уже приличное количество времени, а прогресса в моём деле не так много как хотелось бы...
Почитал, посмотрел множество информации по Godot и его возможностях, установил на данный момент последнюю доступную версию (4.3 stable) через Steam.
Пока что организовал следующую структуру в себя проекте

В папке autoload(автозагрузка) скрипты, которые будут загружаться при старте проекта и доступны из в любом месте для исполнения. В моем случае я пока вынес туда систему сигналов и общий словарь систем доступных в игре и методы работы с этим словарем.

signals.gd

extends Node
signal radioEnabled(currentValue:bool)
signal radioOff
signal airEnabled(currentValue:bool)
signal airOff
signal navigationEnabled(currentValue:bool)
signal navigationOff
signal controlEnabled(currentValue:bool)
signal controlOff
signal climateEnabled(currentValue:bool)
signal climateOff

systems.gd

extends Node
var system:Dictionary = {
'radioEnabled': true,
'airEnabled': true,
'navigationEnabled': true,
'controlEnabled': true,
'climateEnabled': true
}
func getSystem(name:String):
return system.get(name, false)

func setSystem(name:String, value:bool):
var val = null
if system.has(name):
system[name] = value
val = system[name]
return val

В папке components (компоненты) я решил сделать отдельные папки со сценами и скриптами отдельных элементов игры. Попробую на примере, что было понятнее о чем я.
Представим что мы находимся в космическом корабле за пультом управления. Перед нами несколько всяких панелей управления разными системами

-2

Все относятся к панели управления, поэтому я объединил их все в одну папку в компонентах и назвал Panel.
Так как каждая из панелей будет иметь несколько одинаковых методов работы с ними я создал собственный класс в котором описал методы работы панели.

panelSystem.gd

class_name panelSystem
extends Node
var signalSystem:Signal
var signalSystemOff:Signal
var systemStatus:bool
var button:Node2D
var buttonPress:Button
var systemName:String
func _ready() -> void:
systemStatus = Systems.getSystem(systemName)
buttonPress.connect("pressed", _on_change_enabled)
signalSystemOff.connect(_on_system_off)
button.enable(systemStatus)

func _on_change_enabled():
systemStatus = Systems.setSystem(systemName, !systemStatus)
button.enable(systemStatus)
send_current_status()

func _on_system_off():
systemStatus = Systems.setSystem(systemName, false)
button.enable(systemStatus)
send_current_status()
func send_current_status():
signalSystem.emit(systemStatus)

И далее уже при создании сцены конкретной панели например, навигации я просто указал в качестве родителя мой класс и на основе его дополнил логику.

navigation.gd

extends panelSystem
@onready var eButton = $Button
@onready var eButtonPress = $Button/ButtonPress
func _ready() -> void:
button = eButton
buttonPress = eButtonPress
signalSystemOff = Signals.navigationOff
signalSystem = Signals.navigationEnabled
systemName = 'navigationEnabled'
super._ready()

Так же у нас есть одна панель которая по логике отличается от всех остальных.

У меня она называется Electro, по задумке в ней будет выводиться информация о кол-ве свободной энергии которую можно направить например на работу другой системы.

button.gd

extends Node2D
var imgButtonOff:Theme = preload("res://components/Panel/styles/ButtonOff.tres")
var imgButtonOn:Theme = preload("res://components/Panel/styles/ButtonOn.tres")
@onready var buttonPress = $ButtonPress
@onready var isEnabled:bool = true
@onready var soundButtonOn:AudioStreamPlayer2D = $ButtonOn
@onready var soundButtonOff:AudioStreamPlayer2D = $ButtonOff
func enable(enable:bool = true):
isEnabled = true if enable else false
if isEnabled:
buttonPress.theme = imgButtonOn
soundButtonOn.play()
else:
buttonPress.theme = imgButtonOff
soundButtonOff.play()
Пример сцены с одной системой
Пример сцены с одной системой

Так же у нас есть одна панель которая по логике отличается от всех остальных. У меня она называется Electro, по задумке в ней будет выводится информация о кол-ве свободной энергии которую можно направить например на работу другой системы.

Так выглядит сцена
Так выглядит сцена

electro.gd

extends Node2D
@onready var label = $Electro/Label
const maxElectro:int = 100
const oneElemPower:int = 10
var currentElectro:int = 0
func _ready() -> void:
changeLabelText(currentElectro)
Signals.radioEnabled.connect(_on_system_change)
Signals.airEnabled.connect(_on_system_change)
Signals.navigationEnabled.connect(_on_system_change)
Signals.controlEnabled.connect(_on_system_change)
Signals.climateEnabled.connect(_on_system_change)

func changeLabelText(addValue:int = 0, delValue:int = 0):
var newValue = currentElectro
if addValue > 0:
newValue = int(label.text) + addValue
if newValue > maxElectro:
newValue = maxElectro
label.text = str(newValue)
if delValue > 0:
newValue = int(label.text) - delValue
if (newValue < 0):
newValue = protectionSystem(newValue)
label.text = str(newValue)
currentElectro = newValue

func protectionSystem(tmpValue) -> int:
var newPower = tmpValue

if Systems.getSystem('radioEnabled'):
Signals.radioOff.emit()
newPower = calcNewPower(newPower)
if newPower >= 0:
return newPower
if Systems.getSystem('airEnabled'):
Signals.airOff.emit()
newPower = calcNewPower(newPower)
if newPower >= 0:
return newPower
if Systems.getSystem('navigationEnabled'):
Signals.navigationOff.emit()
newPower = calcNewPower(newPower)
if newPower >= 0:
return newPower
if Systems.getSystem('controlEnabled'):
Signals.controlOff.emit()
newPower = calcNewPower(newPower)
if newPower >= 0:
return newPower
if Systems.getSystem('climateEnabled'):
Signals.climateOff.emit()
newPower = calcNewPower(newPower)
if newPower >= 0:
return newPower
return 0

func calcNewPower(currentPower):
return currentPower + oneElemPower
func _on_system_change(value:bool):
if value:
changeLabelText(0, oneElemPower)
else:
changeLabelText(oneElemPower)

Пока копировал и смотрел на этот код понял, что он получится запутанным и скорее всего, это не то место где он должен быть, так что возможно в будущем будет рефактор, но пока он остается таким.

Так же я еще реализовал систему диалога, но об этом уже в следующей статье.