В данной статье используется среда разработки DrRacket. Для начала рассмотрим связь конечного автомата и игрового процесса. Объект управления в игре можно представить в виде конечного автомата. Рассмотрим программу, моделирующую светофор. Этот пример был описан в предыдущей статье.
Переходом в другое устойчивое состояние является переключение сигнала светофора. Диаграмму состояний можно изобразить в следующем виде.
Для того, чтобы создать светофор, нарисуем окружность в центре пустой сцены.
s — это переменная, отвечающая за цвет. Переход в другое состояние можно представить
следующей конструкцией:
Для того, чтобы промоделировать переключение сигнала, используем функцию big-bang.
Теперь светофор переходит в следующее устойчивое состояние 1 раз в секунду. Управляющие воздействие в играх также могут оказывать платформы, препятствия, враги и т.д. Например, в некоторых играх по ходу движения объект может «перескакивать» на следующий канвас, т.е. приблизившись к границе канваса, объект исчезает и появляется на противоположной границе. Условие перехода («перескакивания») определяется сравнением координат объекта и края канваса.
Если условие не выполнено, остаёмся на том же экране.
Движение определяется приращением DELTA к координате x. Напишем программу целиком:
На этой странице представлены примеры программ, использующих big-bang. Запустить программы можно в режиме «Начинающий студент», добавив необходимые пакеты.
Далее напишем программу, в которой управление объектом осуществляется
клавишами «left» и «right».
Здесь нам понадобится функция для обработкой клавиш.
Если же нам также необходимо обрабатывать нажатия клавиш «up», «down», то координаты объекта следует хранить в структуре вида:
Напишем программу, в которой объект может перемещаться по горизонтали и вертикали.
Да, но обычно в играх присутствует несколько объектов. Напишем программу, в которой присутствует два объекта. Используем две структуры: world и posn.
Теперь для того, чтобы управлять одним из объектов, добавим функцию обработки клавиш.
Более подробно о создании игр можно прочитать в книге How to Design Worlds.
Переходом в другое устойчивое состояние является переключение сигнала светофора. Диаграмму состояний можно изобразить в следующем виде.
Для того, чтобы создать светофор, нарисуем окружность в центре пустой сцены.
#lang racket
(require picturing-programs)
(define (DOT s) (circle 100 "solid" s))
s — это переменная, отвечающая за цвет. Переход в другое состояние можно представить
следующей конструкцией:
(define (traffic-light-next s)
(cond
[(string=? "red" s) "green"]
[(string=? "green" s) "yellow"]
[(string=? "yellow" s) "red"]))
Для того, чтобы промоделировать переключение сигнала, используем функцию big-bang.
(big-bang "red"
[on-tick traffic-light-next 1]
[to-draw DOT])
Теперь светофор переходит в следующее устойчивое состояние 1 раз в секунду. Управляющие воздействие в играх также могут оказывать платформы, препятствия, враги и т.д. Например, в некоторых играх по ходу движения объект может «перескакивать» на следующий канвас, т.е. приблизившись к границе канваса, объект исчезает и появляется на противоположной границе. Условие перехода («перескакивания») определяется сравнением координат объекта и края канваса.
[(> (+ x DELTA) WIDTH) 0]
Если условие не выполнено, остаёмся на том же экране.
(cond
[(> (+ x dx) WIDTH) 0]
[else (+ x dx)] )
Движение определяется приращением DELTA к координате x. Напишем программу целиком:
#lang racket
(require 2htdp/image)
(require 2htdp/universe)
(define WIDTH 100)
(define DELTA 1)
(define BALL (circle 5 "solid" "red"))
(define MT (empty-scene WIDTH 10))
(define (main x0)
(big-bang x0
[to-draw render]
[on-tick bounce]))
(define (bounce x)
(cond
[(> (+ x DELTA) WIDTH) 0]
[else (+ x DELTA)] ))
(define (render x)
(place-image BALL x 5 MT))
(main 50)
На этой странице представлены примеры программ, использующих big-bang. Запустить программы можно в режиме «Начинающий студент», добавив необходимые пакеты.
Далее напишем программу, в которой управление объектом осуществляется
клавишами «left» и «right».
Здесь нам понадобится функция для обработкой клавиш.
#lang racket
(require 2htdp/image)
(require 2htdp/universe)
(define BACKGROUND (empty-scene 100 100))
(define DOT (circle 10 "solid" "red"))
(define (place-dot-at x)
(place-image DOT x 50 BACKGROUND))
(define (change-func p k) ; обработка клавиш
(cond
[(string=? "left" k)
(- p 5)]
[(string=? "right" k)
(+ p 5)]
[else p]))
( big-bang 50
[to-draw place-dot-at]
[on-key change-func] )
Если же нам также необходимо обрабатывать нажатия клавиш «up», «down», то координаты объекта следует хранить в структуре вида:
(define-struct posn (x y))
(define INIT-WORLD (make-posn 100 100))
Напишем программу, в которой объект может перемещаться по горизонтали и вертикали.
#lang racket
(require picturing-programs)
(define WIDTH 200)
(define HEIGHT 200)
(define BACKGROUND (empty-scene WIDTH HEIGHT))
(define obj1 (circle 10 "solid" "red"))
(define-struct posn (x y)) ;объявляем структуру
(define INIT-WORLD (make-posn 100 100) )
(define (change-current-world-key current-world a-key-event) ;обработка "событий" клавиатуры
(cond
[(key=? a-key-event "up")
(make-posn (posn-x current-world) (- (posn-y current-world) 5))]
[(key=? a-key-event "down")
(make-posn (posn-x current-world) (+ (posn-y current-world) 5))]
[(key=? a-key-event "left")
(make-posn (-(posn-x current-world)5) (posn-y current-world) )]
[(key=? a-key-event "right")
(make-posn (+(posn-x current-world)5) (posn-y current-world) )]
[else current-world]))
(define (redraw current-world)
(place-image obj1
(posn-x current-world)
(posn-y current-world)
BACKGROUND))
(big-bang INIT-WORLD
(on-key change-current-world-key)
(on-draw redraw) )
Да, но обычно в играх присутствует несколько объектов. Напишем программу, в которой присутствует два объекта. Используем две структуры: world и posn.
#lang racket
(require picturing-programs)
(define WIDTH 300)
(define HEIGHT 300)
(define BACKGROUND (empty-scene WIDTH HEIGHT))
(define obj1 (circle 10 "solid" "red"))
(define obj2 (circle 10 "solid" "green"))
(define-struct posn (x y))
(define-struct world [obj1 obj2])
; Инициализация параметров в структуре world
(define INIT-WORLD (make-world (make-posn 100 100) (make-posn 200 100)))
(define (draw-game world)
(place-image
obj1
(posn-x (world-obj1 world))
(posn-y (world-obj1 world))
(place-image
obj2
(posn-x (world-obj2 world))
(posn-y (world-obj2 world))
BACKGROUND)))
(big-bang INIT-WORLD
[to-draw draw-game])
Теперь для того, чтобы управлять одним из объектов, добавим функцию обработки клавиш.
#lang racket
(require picturing-programs)
(define WIDTH 300)
(define HEIGHT 300)
(define BACKGROUND (empty-scene WIDTH HEIGHT))
(define obj1 (circle 10 "solid" "red"))
(define obj2 (circle 10 "solid" "green"))
(define-struct posn (x y))
(define-struct world [obj1 obj2])
(define INIT-WORLD (make-world (make-posn 50 150) (make-posn 250 150)))
(define (change-current-world-key current-world a-key-event)
(make-world (change-obj1 (world-obj1 current-world) a-key-event)
(world-obj2 current-world)))
(define (change-obj1 a-posn a-key-event)
(cond
[(key=? a-key-event "up")
(make-posn (posn-x a-posn) (- (posn-y a-posn) 5))]
[(key=? a-key-event "down")
(make-posn (posn-x a-posn) (+ (posn-y a-posn) 5))]
[(key=? a-key-event "left")
(make-posn (-(posn-x a-posn) 5) (posn-y a-posn) )]
[(key=? a-key-event "right")
(make-posn (+(posn-x a-posn)5) (posn-y a-posn) )]
[else a-posn]))
(define (draw-game world)
(place-image
obj1
(posn-x (world-obj1 world))
(posn-y (world-obj1 world))
(place-image
obj2
(posn-x (world-obj2 world))
(posn-y (world-obj2 world))
BACKGROUND)))
(big-bang INIT-WORLD
(on-key change-current-world-key)
[to-draw draw-game] )
Более подробно о создании игр можно прочитать в книге How to Design Worlds.
Поделиться с друзьями
Комментарии (5)
Wolf4D
18.06.2017 23:20Любопытная статья. Вспомнилась замечательная игра Abuse, где в движок был встроен интерпретатор, а вся внутриигровая логика была реализована на лежащем в почти открытом видел LISP-е. Отдельные товарищи переписывали скролл-шутер в тетрис :)
TheShock
Но ведь светлофор работает в режиме
А не в режиме
То есть Зеленый и Красный переходят в Желтый. А вот Желтый может перейти и в Зеленый и в Красный.
Выглядит, словно вы подгоняете реальность под ограничения архитектуры. Ведь функцию только от текущего состояния написать легче, чем от текущего+предыдущего. И это на столь легком примере!
TheShock
Кстати, я, конечно, понимаю, что в данном конкректном примере это очень легко пофиксить введя два разных «желтых»: «yellow-before-red» и «yellow-before-green» и машина состояний останется такой же простой, но это ведь абстрактный пример, а в играх очень редко бывает возможность обойтись таким простым решением.
kez
Следующее состояние светофора основывается на нескольких предыдущих, поэтому нужно хранить список (хотя бы двух предыдущих) состояний и явно передавать его в функцию: