This 2 hour workshop will gave you an introduction and overview to programming, programming with Clojure and developing simple games.
We will started with an existing game template and then made changes and saw the effects in real time.
Then we will talked about how simple 2D games are structured and introduce more technical game and programming concepts and aspects.
And then worked on making more changes and customizations.
11. Change Player
1. Open core.clj go to line 25.
2. Change "player.png" to "tram.png"
3. Save the file
(defscreen main-screen
:on-show
(fn [screen entities]
(update! screen :renderer (stage) :camera (orthographic))
(add-timer! screen :spawn-enemy 0 2)
(assoc (texture "player.png") ;; make this "tram.png"
:player? true
:x (/ (game :width) 2)
:y 10
:width 64
:height 64))
11 / 57
12. Change the enemy
1. Open entities.clj
2. Go to line 57
3. Change "enemy.png" to "needle.png"
(defn create-enemy
"Returns an enemy at a random position."
[]
(assoc (texture "enemy.png") ;; make this "needle.png"
:enemy? true
:x (rand (game :width))
:y (game :height)
:width 6 4
:height 64))
12 / 57
13. Change the missile
Starts on line 24 in entities.clj
(defn create-missile
"Returns a missile at the same x position as the mouse."
[]
(assoc (shape :filled
:set-color (color :blue) ;; make :red
:circle 0 0 10) ;; :ellipse 0 0 10 50
:missile? true
:x (game :x)
:y 50 ;; make 70
:width 10
:height 10)) ;; make 50
13 / 57
14. Play a sound when the missile
fires
(defn create-missile
"Returns a missile at the same x position as the mouse."
[]
(sound "laser.wav" :play) ;; Add this
(assoc (shape :filled
:set-color (color :red)
:ellipse 0 0 10 70)
:missile? true
:x (game :x)
:y 70
:width 20
:height 70))
14 / 57
15. Play background music
Back in core.clj around line 21
(defscreen main-screen
:on-show
(fn [screen entities]
(sound "music.wav" :loop) ;; Add this
(update! screen :renderer (stage) :camera (orthographic))
(add-timer! screen :spawn-enemy 0 2)
(assoc (texture "tram.png")
:player? true
:x (/ (game :width) 2)
:y 10
:width 64
:height 64))
...
*Originally called http://ccmixter.org/files/djlang59/37792
15 / 57
21. The game loop - Hollywood
principle
Don't call us we'll call you.
Every time its time to draw a new screen :on-render (line 32) is called
:on-render
(fn [screen entities]
(clear!)
(->> entities
(move-missiles)
(move-enemies)
(update-score!)
(remove-missiles)
(remove-enemies)
(remove :remove?)
(render! screen)))
21 / 57
22. Move missiles
entities.clj line 37
(defn move-missiles
"Moves the missiles up."
[entities]
(for [e entities]
(if (:missile? e)
(assoc e :y (+ (:y e) 5))
e)))
22 / 57
23. Move enemies
entities.clj line 65
(defn move-enemies
"Moves the enemies down."
[entities]
(for [e entities]
(if (:enemy? e)
(assoc e :y (- (:y e) 3))
e)))
23 / 57
24. Clojure Maps - A little bundle of
information
Other languages call them hash maps, hash or dictionary.
Maps associate keys and values.
Curly braces, {}, denote maps.
;; a map or record for a person
{:first-name "Julio"
:last-name "Barros"
:age 29}
;; a map for a game entity
{:texture-info "some stuff the system builds for us"
:missile? true
:x 23
:y 30
:color :red}
24 / 57
25. The REPL
Open the repl and type (+ 2 4) and {:name "your name here"}
25 / 57
26. assoc
Associates (adds or updates) a key and value in one map creating a new map.
(assoc {:name "Julio" } :loves "Clojure")
;; results in - ie outputs
{
:name "Julio"
:loves "Clojure"
}
(assoc {:x 10, :color :red} :x 11)
;; results in
{
:color :red
:x 11
}
26 / 57
27. Getting values from a map
(get {:y "because"} :y) ;; gets the value of y
(:y {:y "because"}) ;; same thing just shorter
({:y "because"} :y) ;; this works too
27 / 57
28. What's with all those round ()
things?
Parentheses (round braces) denote lists. Lists are a sequence of things.
The special thing about lists is that usually the first item is executed.
(action argument1 argument2)
(function parameter1 parameter2)
(+ 3 4)
(assoc e :x 33)
(assoc {:x 22, :color :red} :x 33)
quote or ' keeps the list from being executed
'(1 2 3) ;; [1 2 3] more common
28 / 57
29. What's up with the square []
things?
Square brackets, [] denote a vector (array). Vectors are also a sequence.
[1 2 3]
[1 2 [3 4]]
(count [5 5 5])
=> 3
(get [1 2 3] 1) ;; Note: zero based.
=> 2
You can have any length vector (or list) made up of any "type"
[3 {:name "Joe"} [1 2 3] '(1 2 [3 5])]
'(3 {:name "Joe"} [1 2 3] '(1 2 [3 5]))
29 / 57
30. Other interesting types
"I'm a string" ;; strings
:name ;; keyword
3 ;; integer
3.0 ;; double
;; Note: 3.0 is different than "3.0"
0.5 ;; must start with a number
{} ;; map
[] ;; vectors
() ;; lists
#{} ;; set - unordered collection with no duplicates
30 / 57
32. for
Walks through vector/sequence giving each element the name "e" and using it in the steps
that follow.
"e" can be any name.
Returns a vector.
(for [e [1 2 3]]
(+ e e))
=> (2 4 6)
(for [current-n [1 2 3]]
(even? current-n))
=> (false true false)
(for [e entities]
(if (:missile? e)
(assoc e :y (+ (:y e) 5))
e))
32 / 57
33. Functions
Takes zero or more input parameters and returns a single value.
The return value could be a list or map or vectors of multiple things.
Returns the result of the last expression executed.
(defn my-function-name
"A documentation string"
[param1 param2]
(functionA param1)
(functionB param1 param2))
(defn increment
"Adds one to the parameter"
[a-number]
(+ 1 a-number))
(increment 3)
=> 4
33 / 57
35. Create an enemy
(defn create-enemy
"Returns an enemy at a random position."
[]
(assoc (texture "needle.png")
:enemy? true
:x (rand (game :width))
:y (game :height)
:width 64
:height 64))
(create-enemy)
{:object "... some stuff about com.badlogic.gdx.graphics.g2d.TextureRegion"
:height 64,
:width 64,
:y 965,
:x 173.37581878372418,
:enemy? true}
35 / 57
36. Random numbers
Random numbers are super useful in games.
(rand-int n) returns a random integer between 0 (inclusive) and n (exclusive).
(rand-int 10)
=> 3 ;; or 0 or 1 or 2 .. or 9
36 / 57
37. Exercise: Our own Function -
choose-one
Write a function called choose-one that randomly chooses one element from a vector
(choose-one [3 6]) chooses either 3 or 6 randomly. Put the function at the top of entities.clj
Things to think about:
1. How do you know how many things are in the vector?
2. How do you pick an index between 0 and the last index in the vector?
3. How do you get the item in the vector at that index?
37 / 57
39. Step 1
Figure out how many things we have to choose from.
(defn choose-one
"Pick and return one thing from v. Well just the number of things in v."
[v]
(count v))
39 / 57
40. Step 2
Pick a random index - a number from 0 to the number of things we have to choose from.
40 / 57
41. Step 2
Pick a random index - a number from 0 to the number of things we have to choose from.
(defn choose-one
"Pick and return one thing from v. Well a random valid index into v."
[v]
(rand-int (count v)))
41 / 57
42. Step 3
Get the item at that index in the vector.
42 / 57
43. A solution
Get the item at that index in the vector
(defn choose-one
"Pick and return one thing from v."
[v]
(get v (rand-int (count v))))
43 / 57
44. Use choose-one to pick different
enemies
(defn create-enemy
"Returns an enemy at a random position."
[]
(assoc (texture (choose-one ["enemy.png","needle.png"]))
:enemy? true
:x (rand (game :width))
:y (game :height)
:width 6 4
:height 64))
44 / 57
45. Exercise: one-in
Write a function called one-in that takes a number, n, and returns true 1 in n times. (one-in
5) returns true ~20% (1 in 5) of the time
Use it to create an old style enemy 1 out of 10 times.
Can you use choose-one to do the same thing?
45 / 57
46. Back to the game loop
Every function takes a list of entities and returns a list of entities.
May be the same list. More likely a new list made from the old list with a few changes.
(->> entities
(move-missiles)
(move-enemies)
(update-score!)
(remove-missiles) ;; mark missiles for removal
(remove-enemies) ;; mark enemies for removal
(remove :remove?) ;; actually do the remove
(render! screen)))
The remove step logic is a little tricky.
Homework: Study it to see if you can figure out how it works.
46 / 57
48. Programmable Music - Sonic Pi
http://sonic-pi.net
http://vimeo.com/113811476
48 / 57
49. Today's Parts Review
Clojure - Modern LISP targeting the Java Virtual Machine (JVM). http://clojure.org
Java / JVM - Mature enterprise programming language and the system that runs it.
https://www.java.com/
Nightmod - Tool for making live-moddable games using Clojure and play-clj.
https://nightmod.net
play-clj - Clojure game library that uses libGDX. https://github.com/oakes/play-clj
libGDX - Game development framework written in Java. http://libgdx.badlogicgames.com
49 / 57
50. Clojure Resources
Clojure Cheat Sheet
http://clojure.org/cheatsheet
Clojure From The Ground Up
https://aphyr.com/tags/Clojure-from-the-ground-up
Clojure For the brave and true
http://www.braveclojure.com
ClojureBridge
https://github.com/ClojureBridge/curriculum
Nightmod / Nightcode - thank Zach
https://nightmod.net
Emacs, Clojure Cursive, LightTable - other environments (IDEs).
50 / 57
51. Other popular languages
Javascript - Runs in the browser.
ClojureScript - Clojure for Javascript.
Ruby - Popular language with startups.
Python - Popular for data science.
Many many more.
Find one you (and your friends) like and form a
study group.
51 / 57
55. Thank You
Julio Barros - http://twitter.com/JulioBarros
Justin Holguin
Jed Clinger
Nuatu Tseggai
55 / 57
56. Feedback
1. What went well?
2. What can be improved?
3. How do we reach more people?
56 / 57
57. Exercise: Better Motion
1) Make the enemies drift to the right as they fall.
2) Make some enemies go much faster than other enemies
3) Make enemies zig zag in a smooth(ish) manner as they fall.
57 / 57