Проекты в папке 2d в Godot просмотрены.Я хотел бы закрепить уроки примерами игр.godot 2d игра.Сокобан 1
godot 2d игра.Сокобан.Описание
Начну с поиска изображений для первой игры — Сокобан. Это классическая головоломка, в которой нужно перемещать коробки по складу на отмеченные места. Как только все коробки окажутся на нужных позициях, уровень считается пройденным. Вот пример того, как выглядит типичный уровень
На этом уровне игрок загнал ящик в угол, что делает прохождение невозможным — игра проиграна. Визуальное оформление: стены отображаются красным цветом, коробки, ещё не стоящие на месте — серым, а коробки, размещённые на нужных позициях — зелёным. Персонаж представлен в виде грузчика
Сокобан.Структура
Я намечу структуру игры. Она будет состоять из корневого узла Node2D с вложенным TileMapLayer, к которому будет подключён скрипт, управляющий расположением блоков на карте
Создадим проект Sokoban experiment. Добавим на сцену корневой узел Node2D, внутрь которого поместим TileMapLayer. Затем загрузим тайлы для карты и подключим скрипт управления
Сохраним сцену как game.tscn. Обратим внимание на идентификаторы тайлов: 0 — для коробки, 1 — для кирпичной стены, 2 — для игрока. Позицию игрока можно получить через функцию get_used_cells_by_id(2), которая возвращает массив Vector2i с координатами всех тайлов с заданным идентификатором. Так как игрок на сцене один, берём нулевой элемент этого массива. Добавим код
extends TileMapLayer
var pos:Vector2i
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
print(get_cell_source_id(Vector2i(0,0)))
print(get_cell_source_id(Vector2i(3,1)))
pos=get_used_cells_by_id(2)[0]
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
if(Input.is_action_just_released("ui_up")):
var v = pos+Vector2i(0,-1)
var n = pos+Vector2i(0,-2)
if(get_cell_source_id(v)!=1):
if(get_cell_source_id(n)!=0 or get_cell_source_id(v)!=0):
set_cell(pos)
if(get_cell_source_id(v)==0):
if(get_cell_source_id(n)!=1):
set_cell(n,0,Vector2i(0,0))
else:
pos+=Vector2i(0,1)
pos+=Vector2i(0,-1)
set_cell(pos,2,Vector2i(0,0))
if(Input.is_action_just_released("ui_down")):
var v = pos+Vector2i(0,1)
var n = pos+Vector2i(0,2)
if(get_cell_source_id(v)!=1):
if(get_cell_source_id(n)!=0 or get_cell_source_id(v)!=0):
set_cell(pos)
if(get_cell_source_id(v)==0):
if(get_cell_source_id(n)!=1):
set_cell(n,0,Vector2i(0,0))
else:
pos+=Vector2i(0,-1)
pos+=Vector2i(0,1)
set_cell(pos,2,Vector2i(0,0))
if(Input.is_action_just_released("ui_right")):
var v = pos+Vector2i(1,0)
var n = pos+Vector2i(2,0)
if(get_cell_source_id(v)!=1):
if(get_cell_source_id(n)!=0 or get_cell_source_id(v)!=0):
set_cell(pos)
if(get_cell_source_id(v)==0):
if(get_cell_source_id(n)!=1):
set_cell(n,0,Vector2i(0,0))
else:
pos+=Vector2i(-1,0)
pos+=Vector2i(1,0)
set_cell(pos,2,Vector2i(0,0))
if(Input.is_action_just_released("ui_left")):
var v = pos+Vector2i(-1,0)
var n = pos+Vector2i(-2,0)
if(get_cell_source_id(v)!=1):
if(get_cell_source_id(n)!=0 or get_cell_source_id(v)!=0):
set_cell(pos)
if(get_cell_source_id(v)==0):
if(get_cell_source_id(n)!=1):
set_cell(n,0,Vector2i(0,0))
else:
pos+=Vector2i(1,0)
pos+=Vector2i(-1,0)
set_cell(pos,2,Vector2i(0,0))
Разбор кода
Теперь разберём этот код подробнее
var v = pos+Vector2i(0,-1)
var n = pos+Vector2i(0,-2)
Этот код вычисляет позицию на один и два блока выше текущего местоположения игрока
if(get_cell_source_id(n)!=0 or get_cell_source_id(v)!=0):
Проверяются два условия: отсутствие двух блоков, поставленных друг на друга, и отсутствие стены непосредственно перед игроком
set_cell(pos)
Затем стираем старую позицию игрока
if(get_cell_source_id(v)==0):
Проверяем, находится ли корзина на клетке, расположенной на один блок выше игрока
if(get_cell_source_id(n)!=1):
Проверяем, отсутствует ли стена на клетке, расположенной на два блока выше игрока
set_cell(n,0,Vector2i(0,0))
Если условия выполнены, перемещаем корзину на одну клетку вверх
else:
pos+=Vector2i(0,1)
Затем возвращаем игрока на исходную позицию, чтобы корзина не оказалась под ним
pos+=Vector2i(0,-1)
перемещение игрока
set_cell(pos,2,Vector2i(0,0))
Теперь ставим игрока на ту клетку, куда он должен переместиться
Проснувшись однажды утром после беспокойного сна, Грегор Замза обнаружил, что он у себя в постели превратился в страшное насекомое.
Теперь нам осталось добавить два элемента: целевое место для коробки и индикатор правильно установленной коробки. Добавляем эти два блока в тайлсет. Я модифицировал код, чтобы теперь можно было размещать блоки на нужных позициях
Вот полный код игры — от инициализации карты до логики перемещения игрока и коробок
extends TileMapLayer
var pos:Vector2i
var goals:Array
var old_goals:Array
var boxes:Array
var boxes2:Array
var block:Array
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pos=get_used_cells_by_id(2)[0]
goals=get_used_cells_by_id(4)
pass # Replace with function body.
# Called every frame. ‘delta’ is the elapsed time since the previous frame.
func _process(delta: float) -> void:
boxes=get_used_cells_by_id(0)
boxes2=get_used_cells_by_id(3)
boxes.append_array(boxes2)
block=[]
for i in range(len(boxes)):
for j in range(len(goals)):
if(boxes[i]==goals[j]):
set_cell(boxes[i],0,Vector2i(0,0))
if(Input.is_action_just_released(«ui_up»)):
var v = pos+Vector2i(0,-1)
var n = pos+Vector2i(0,-2)
if(get_cell_source_id(v)!=1):
if(get_cell_source_id(n)!=0 or get_cell_source_id(v)!=0):
set_cell(pos)
if(get_cell_source_id(v)==0):
if(get_cell_source_id(n)!=1):
set_cell(n,0,Vector2i(0,0))
else:
pos+=Vector2i(0,1)
pos+=Vector2i(0,-1)
set_cell(pos,2,Vector2i(0,0))
if(Input.is_action_just_released(«ui_down»)):
var v = pos+Vector2i(0,1)
var n = pos+Vector2i(0,2)
if(get_cell_source_id(v)!=1):
if(get_cell_source_id(n)!=0 or get_cell_source_id(v)!=0):
set_cell(pos)
if(get_cell_source_id(v)==0):
if(get_cell_source_id(n)!=1):
set_cell(n,0,Vector2i(0,0))
else:
pos+=Vector2i(0,-1)
pos+=Vector2i(0,1)
set_cell(pos,2,Vector2i(0,0))
if(Input.is_action_just_released(«ui_right»)):
var v = pos+Vector2i(1,0)
var n = pos+Vector2i(2,0)
if(get_cell_source_id(v)!=1):
if(get_cell_source_id(n)!=0 or get_cell_source_id(v)!=0):
set_cell(pos)
if(get_cell_source_id(v)==0):
if(get_cell_source_id(n)!=1):
set_cell(n,0,Vector2i(0,0))
else:
pos+=Vector2i(-1,0)
pos+=Vector2i(1,0)
set_cell(pos,2,Vector2i(0,0))
if(Input.is_action_just_released(«ui_left»)):
var v = pos+Vector2i(-1,0)
var n = pos+Vector2i(-2,0)
if(get_cell_source_id(v)!=1):
if(get_cell_source_id(n)!=0 or get_cell_source_id(v)!=0):
set_cell(pos)
if(get_cell_source_id(v)==0):
if(get_cell_source_id(n)!=1):
set_cell(n,0,Vector2i(0,0))
else:
pos+=Vector2i(1,0)
pos+=Vector2i(-1,0)
set_cell(pos,2,Vector2i(0,0))
for i in range(len(goals)):
if(get_cell_source_id(goals[i])!=0 and get_cell_source_id(goals[i])!=2):
set_cell(goals[i],4,Vector2i(0,0))
for i in range(len(goals)):
if(get_cell_source_id(goals[i])==0):
set_cell(goals[i],3,Vector2i(0,0))
Код, добавленный в начало файла
boxes=get_used_cells_by_id(0)
Получаем все коробки на карте с помощью метода get_used_cells_by_id()
boxes2=get_used_cells_by_id(3)
Получаем все коробки, которые уже находятся на целевых позициях
boxes.append_array(boxes2)
Найденные коробки мы добавляем в общий список, чтобы в конце проверить, все ли они стоят на целевых позициях
block=[]
for i in range(len(boxes)):
for j in range(len(goals)):
if(boxes[i]==goals[j]):
set_cell(boxes[i],0,Vector2i(0,0))
После вычисления новой позиции устанавливаем коробку на соответствующую клетку
В конце файла добавляем следующий код
for i in range(len(goals)):
if(get_cell_source_id(goals[i])!=0 and get_cell_source_id(goals[i])!=2):
set_cell(goals[i],4,Vector2i(0,0))
for i in range(len(goals)):
if(get_cell_source_id(goals[i])==0):
set_cell(goals[i],3,Vector2i(0,0))
godot 2d игра.Сокобан.Конец
Спасибо за внимание! С вашей помощью я разработал игру и закрепил навыки работы с тайлсетами. Благодарю за внимание!





