play-clj试用体验

play-clj是一个基于libgdx的clojure framework. libgdx是个很有名的跨平台游戏开发组件。如果你不用unity等商业引擎,那么多半只能选择它。CLOJURE是一个lisp的jvm语言。

titled

废话少说,先跟着作者的教程写个进入界面title screen。

(ns day01.core
(:require
[day01.enter-menu :as menu]
[day01.main-screen :as main]
[play-clj.core :refer :all]
[play-clj.ui :refer :all]))

(declare day01)

(defscreen title-screen
:on-show
(fn [screen entities]
(update! screen :renderer (stage))
[(menu/place-logo 220 220)
(menu/place-end-bn 150 50)
(menu/place-start-bn 150 50)
])

:on-render
(fn [screen original-entities]
(clear!)
(let [entities
(-> original-entities
menu/render-start-bn
menu/render-end-bn)]
(render! screen entities)
original-entities))

:on-key-down
(fn [screen entities]
(cond
(= (:key screen) (key-code :dpad-up))
(menu/key-start-bn entities)
(= (:key screen) (key-code :dpad-down))
(menu/key-end-bn entities)
(= (:key screen) (key-code :enter))
(menu/key-enter entities day01 main/main-screen)
(= (:key screen) (key-code :dpad-right))
(println "right")
(= (:key screen) (key-code :dpad-left))
(println "left")))
)

(defgame day01
:on-create
(fn [this]
(set-screen! this title-screen)))

很简单的三个系统: 初始,渲染,输出输入。

好了,那么就是游戏的主循环和entity了。随便弄个主角,当然是小麦芽大人。

(ns day01.main-screen
(:require
[day01.char-keyboard :as k]
[day01.physics :as p]
[play-clj.core :refer :all]
[play-clj.ui :refer :all]
[play-clj.g2d :refer :all]))

(defn place-char [w h]
(assoc (texture "char.gif")
:x (- (/ (game :width) 2) (/ w 2))
:oldx (- (/ (game :width) 2) (/ w 2))
:y (- (/ (game :height) 2) (/ h 2))
:oldy (- (/ (game :height) 2) (/ h 2))
:width w
:height h
:guid :char))

(defn place-bg [w h]
(assoc (texture "walltilemap.jpg")
:x 0
:oldx 0
:y 0
:oldy 0
:width w
:height h
:guid :bg))

(defscreen main-screen
:on-show
(fn [screen entities]
(update! screen
:camera (orthographic)
:renderer (stage))
[(place-bg (game :width) (game :height))
(place-char 50 76)])

:on-render
(fn [screen entities]
(clear!)
(->> entities
k/key-control
p/physics
(render! screen)))

:on-key-down
(fn [screen entities]
(cond
(= (:key screen) (key-code :escape)) (System/exit 0)))

:on-resize
(fn [screen entities]
(height! screen 600))
)

然后是物理碰撞。就自己随便鼓捣个类似tile map的东东吧:

walltilemap

先弄个像素地图。然后把它load进去我的“物理引擎”:

(ns day01.physics
(:require
[play-clj.core :refer :all]))

(def collide-map "resources/walltilemap.bmp")

(defn collide-2d-list [file-str]
"this function shows how easy to get pixel value"
(let [img (javax.imageio.ImageIO/read (java.io.File. file-str))
h (.getHeight img)
w (.getWidth img)]
(for [y (range h)]
(for [x (range w)]
(cond
(= (.getRGB img x y) -1) false
(= (.getRGB img x y) -16777216) true)))))

(defn collide? [entity collide-map]
(let [img (javax.imageio.ImageIO/read (java.io.File. collide-map))
screen-w (game :width)
screen-h (game :height)
w (.getWidth img)
h (.getHeight img)
tile-w (/ screen-w w)
tile-h (/ screen-h h)
filp-y (dec (- screen-h (:y entity)))
tile-x (Math/floor (/ (:x entity) tile-w))
tile-y (Math/floor (/ filp-y tile-h))
pixel (.getRGB img tile-x tile-y)
result (if (not (= pixel -1)) true false)
]
result))

(defn physics [entities]
(map (fn [entity]
(if (collide? entity collide-map)
(assoc entity
;:collide? (collide? entity collide-map)
:x (:oldx entity)
:y (:oldy entity)
)
entity))
entities))

至于游戏背景。。。真麻烦,就用tile map本身。嗯,后现代超写实转人类极简主义。我学了20年美工,这画面太美你没资格评论。

最后是主角的控制:

(ns day01.char-keyboard
(:require [play-clj.core :refer :all]
[play-clj.g2d :refer :all]))

(def speed {:x 3 :y 3})

(defn key-up [entities]
(map (fn [entity]
(if (and (not (:collide? entity)) (= :char (:guid entity)))
(assoc entity
:oldy (:y entity)
:y (+ (:y entity) (:y speed)))
entity))
entities)
)

(defn key-down [entities]
(map (fn [entity]
(if (and (not (:collide? entity)) (= :char (:guid entity)))
(assoc entity
:oldy (:y entity)
:y (- (:y entity) (:y speed)))
entity))
entities)
)

(defn key-left [entities]
(map (fn [entity]
(if (and (not (:collide? entity)) (= :char (:guid entity)))
(assoc entity
:oldx (:x entity)
:x (- (:x entity) (:x speed)))
entity))
entities)
)

(defn key-right [entities]
(map (fn [entity]
(if (and (not (:collide? entity)) (= :char (:guid entity)))
(assoc entity
:oldx (:x entity)
:x (+ (:x entity) (:x speed)))
entity))
entities)
)

(defn key-upleft [entities]
(-> entities
key-up
key-left))

(defn key-upright [entities]
(-> entities
key-up
key-right))

(defn key-downright [entities]
(-> entities
key-down
key-right))

(defn key-downleft [entities]
(-> entities
key-down
key-left))

(defn key-control [entities]
(cond
(and (key-pressed? :dpad-up) (key-pressed? :dpad-left)) (key-upleft entities)
(and (key-pressed? :dpad-up) (key-pressed? :dpad-right)) (key-upright entities)
(and (key-pressed? :dpad-down) (key-pressed? :dpad-left)) (key-downleft entities)
(and (key-pressed? :dpad-down) (key-pressed? :dpad-right)) (key-downright entities)
(key-pressed? :dpad-up) (key-up entities)
(key-pressed? :dpad-down) (key-down entities)
(key-pressed? :dpad-left) (key-left entities)
(key-pressed? :dpad-right) (key-right entities)
:else entities
))

好!完工。能动,能碰撞,差不多了:

“Daddy HELP! Why you put me in such a crappy place!!??”
gg

总结:

play-clj的结构很简单。主要是基于clojure的presistant data-structure 和 Entity-component-system的思维。可惜自带的物理引擎是box2d和子弹physis, 太过于OOP,显得和clojure格格不入,所以我就没用它们。还有就是各种map, reduce,估计大型游戏太耗资源。不过大型游戏跟我毛关系没有。况且clojure 1.7还有transducer的加入来解决性能问题,我就不瞎操心了。

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: