New Kojo Release — 2.9.27, plus more…
I’m pleased to announce a new release of Kojo. The following are the highlights (for Kojo and the activities around it) since the last announcement:
- Functional gaming
- Object-oriented gaming with kojo-gaming
- kojo-ai
- kojo-music
- Picture clipping and masking
- Other miscellaneous enhancements
Functional gaming
Let’s start with a simple animation that runs via the animateWithRedraw
command:
cleari()
case class Model(n: Int)
def initState = Model(1)
def update(m: Model) = m.copy(n = m.n + 1)
def view(m: Model) = Picture.text(m.n, 30)
animateWithRedraw(initState, update, view)
With animateWithRedraw
, you need to define the following:
- The state of your animation (i.e. the stuff that changes as your animation runs) – defined here via a
Model
case class. - an
init
function that returns your initial state. - an
update
function that returns the next state given the previous state. - a
view
function that returns a picture given a state (the picture is the visual representation of the state).
The animation is defined in a purely functional way with the above. To run it and show it on the screen, you call the animateWithRedraw
command, passing it the init
, update
, and view
functions.
As the animation runs, it increments a counter in the center of the screen (do you see why?).
Now, what you need (in general) to convert an animation into a game is interactivity, so that messages (like key presses and mouse clicks) from outside can get into your program, enabling you to react to them.
Let’s do a simple cut of this with our animation above. Let’s just make the increasing counter move left/right on key presses, until it moves more than 100 pixels away from the origin:
cleari()
val speed = 5
val limit = 100
case class Model(n: Int, x: Double, y: Double)
trait Msg
case object Tick extends Msg
case object Left extends Msg
case object Right extends Msg
case object Still extends Msg
def initState = Model(1, 0, 0)
def update(m: Model, msg: Msg) = msg match {
case Tick =>
m.copy(n = m.n + 1)
case Left =>
m.copy(x = m.x - speed)
case Right =>
m.copy(x = m.x + speed)
case Still =>
m
}
def view(m: Model) = Picture.text(m.n, 30).withTranslation(m.x, m.y)
val tickSub: Sub[Msg] = Subscriptions.onAnimationFrame {
Tick
}
val keySub: Sub[Msg] = Subscriptions.onKeyDown {
case Kc.VK_LEFT => Left
case Kc.VK_RIGHT => Right
case _ => Still
}
def subs(m: Model) = {
if (m.x > -limit && m.x < limit) Seq(tickSub, keySub)
else Seq()
}
runGame(initState, update, view, subs)
activateCanvas()
Let us see what’s changed compared to the pure animation:
- You still have a
Model
case class to represent the state of the game. This contains anx
andy
now in addition to the counter value. - You have a bunch of case classes / objects to represent the possible messages that can come into your game.
- You still have an
init
function that returns your initial state. - You still have an
update
function that returns the next state given the previous state. But this function now takes an additional message paramter. - You still have a view function as before.
- You have a new
subs
function that returns subscriptions for messages that the game is interested in, given the current state of the game. These subscriptions deliver the desired messages into the game.
And there, you have the essentials of a purely functional game!
Based on these ideas, you can see a fuller game here.
Note – Functional gaming in Kojo is a proof of concept prototype (partially based on ideas derived from elm). Many thanks to Anay Kamat for his inputs in this area.
Object-oriented gaming with kojo-gaming
kojo-gaming is a module/extension for Kojo, built with the goal of exposing older students (11th grade, 2nd/3rd year undergrad, and older) to core ideas in practical coding – in an interesting context, with the building of fun (hardware accelerated) games within Kojo that they can export to Desktop and Mobile.
kojo-gaming is based on the object-oriented paradigm (this works very well for gaming in our mind, plus is also widely used in industry), with functional ideas coming in as needed to improve code.
The core ideas in kojo-gaming are the following:
- A simple entity component system, with the following features:
- animated sprites with rendering based on textures, texture atlases, and particle systems.
- simple physics (kinematics).
- collision detection and response.
- world boundary behaviors (bouncing, wrapping, etc).
- and more…
- A couple of different implementations:
- One that works right within the Kojo canvas – for a quick and simple start, and the easy learning of fundamental ideas. This is called
game-engine-lite
- One that is based on libGDX – for putting the fundamental ideas to use in creating fancier games that can be exported to Desktop and Mobile. In this approach, Kojo brings up a new window where all the action happens.
- One that works right within the Kojo canvas – for a quick and simple start, and the easy learning of fundamental ideas. This is called
All of this will be described in an upcoming post. For now, to get going, you can just download the game-engine-lite.zip
file, unzip it anywhere, and then open/run any of the samples under game-engine-lite/samples
. Or you can download the full release of kojo-gaming.
kojo-ai
kojo-ai is a module/extension for Kojo, built with the goal of providing older students a good learning ground for AI. kojo-ai provides support for things like the following:
- Learning the essential theoretical concepts in AI.
- Playing with these concepts within engaging projects.
- Getting hands-on with the Learning (Training) process of AI models.
- Building AI-enabled applications.
- Using pre-trained AI models within scripts.
- Using Generative AI Apps for design work for games written in Kojo.
- Gaining insights into generative AI and large-scale foundational models (like ChatGPT).
More on this in an upcoming blog post. For now, to play with kojo-ai, you can use the latest release and provided instructions.
kojo-music
kojo-music is a module/extension for Kojo, built with the following basic goals:
- Provide music as a rich area for children to do creative coding.
- Enable children to have fun with music as creators and composers - via coding.
- Enable the usage of Kojo/coding as a new kind of musical instrument.
kojo-music aims to provide an easy start via a Music Workbench, where musical compositions can be created and played, and then exported as code – to enable a gentle entry into coding for music.
kojo-music uses Alda as its music server.
kojo-music will be described in detail in an upcoming blog post. In the meantime, to play with kojo-music you can use the latest release and provided instructions.
Picture clipping and masking
As a quick reminder, Pictures are Kojo’s way of doing functional graphics. To work with Pictures:
- You create one or more pictures using predefined functions (for various shapes), or by using the turtle-graphics API, or by using lower level primitives from Java2D.
- You transform these pictures using various predefined transforms like translation, rotation, and scaling.
- You combine these pictures in rows, columns, and stacks as desired (with rows/columns/stacks of pictures themselves being pictures).
- And then you finally draw these pictures on the canvas (or use them in animations or games).
Clipping and Masking are a couple of new transforms for Pictures. Let’s get right to them and see them in action.
The following is our base picture (to which clipping and masking will be applied):
cleari()
setBackground(ColorMaker.hsl(0, 0.00, 0.12))
val w = 400
val h = 200
val fill = cm.linearGradient(0, 0, red, 0, h, yellow)
val rect = Picture.rectangle(w, h).withFillColor(fill)
val rectWithReflection = picStack(
rect,
rect.withFading(h)
.withTranslation(0, 3)
.withFlippedY
)
drawCentered(rectWithReflection)
Let’s now clip the rectangle with a circle (so that only the portion of the rectangle that lies within the circle is visible):
cleari()
setBackground(ColorMaker.hsl(0, 0.0, 0.12))
val w = 400
val h = 200
val fill = cm.linearGradient(0, 0, red, 0, h, yellow)
val rect = Picture.rectangle(w, h).withFillColor(fill)
val rectWithReflection = picStack(
rect,
rect.withFading(h)
.withTranslation(0, 3)
.withFlippedY
)
val clippingPic = Picture.ellipseInRect(w, h * 2).withTranslation(0, -h)
val clippedPic =
rectWithReflection
.withClipping(clippingPic)
.withPenColor(black)
.withPenThickness(4)
drawCentered(clippedPic)
Finally, let’s apply a mask to the clipped picture. The mask is a black/white (or grayscale) picture, where black specifies transparent regions, and white specifies opaque regions (with gray in between, with darker grays being more transparent):
cleari()
setBackground(ColorMaker.hsl(0, 0.0, 0.12))
val w = 400
val h = 200
val fill = cm.linearGradient(0, 0, red, 0, h, yellow)
val rect = Picture.rectangle(w, h).withFillColor(fill)
val rectWithReflection = picStack(
rect,
rect.withFading(h)
.withTranslation(0, 3)
.withFlippedY
)
val clippingPic = Picture.ellipseInRect(w, h * 2).withTranslation(0, -h)
val clippedPic =
rectWithReflection
.withClipping(clippingPic)
.withPenColor(black)
.withPenThickness(4)
val maskPic = picStack(
Picture.rectangle(w, h * 2).withFillColor(white).withPenColor(white).withPenThickness(4),
Picture.arc(w / 2, 180).withRotation(90).withScaling(0.9).withTranslation(w+3, h).withFillColor(black).withPenColor(black),
Picture.arc(w / 2, 180).withRotation(-90).withScaling(0.9).withTranslation(-3, h).withFillColor(black).withPenColor(black)
)
val maskedPic = clippedPic.withMask(maskPic)
drawCentered(maskedPic)
Other miscellaneous enhancements
There have been many other enhancements since the last release announcement. Some of these are mentioned below (without a lot of detail for now – we’ll have explanatory blog posts on some of these in the future):
- Realtime note playing with the
setNoteInstrument(instrument)
andplayNote(note, duration)
commands. The Showcase -> Fireworks sample in Kojo has a non-trivial example of this. - Using
CanvasDraw
in animations.CanvasDraw
offers a Processing-like api for simple shape drawing, and is useful where you don’t want retained-mode graphics (which is what you get with Pictures).CanvasDraw
is used bygame-engine-lite
in kojo-gaming. - Support for drawing text and images in
CanvasDraw
. - Support for spawning external processes from within Kojo. To use this, your script should contain the
#exec
pragma at the top. This feature is used by kojo-gaming to spawn external libGDX processes. - Improved curve drawing.
- Neural Style Transfer refinements.
- Apple silicon support.
- New samples.
- Turkish enhancements.
That’s it for now…
As always, the new version of Kojo is available from the Kojo Download Page. If you run into any difficulties, let us know.
Enjoy!
Enjoy Reading This Article?
Here are some more articles you might like to read next: