Try   HackMD

Artful Coding 2 - Session Plans

Session overview

7 x 1.5 hours input and learning sessions:

  • HTML Canvas: intro , 16 March 2022
  • HTML Canvas: animated and interactive , 23 March 2022
  • Jumping Block in Canvas , 30 March 2022
  • Live Server & Phaser init & Jumping Block in Phaser, 06 April 2022
  • Website Mini Game , 27 April 2022
  • Infinite Duality Jumper , 04 May 2022
  • [buffer] , 11 May 2022

3 x 3.0 hours concept & work sessions

  • Brainstorming game ideas and team formation , 25 May 2022, 15:00–18:00 Conference Room 11
  • Concept refinement & first implementation steps , 08 June 2022, 15:00–18:00 Conference Room 11
  • Implementation ctd. , project discussion , closing review , 22 June 2022, 15:00–18:00 Seminar Room 23

Session 2 - HTML Canvas: intro

Reference documents for this session:

Session plan:

  1. Input stage: walk through the W3schools tutorial with some side notes on the more extensive MDN tutorial
  2. Workshop stage: create an HTML page with a canvas and draw the following into the canvas
  • a diagonal line
  • some more random lines with different widths and colors
  • a full circle
  • a half filled circle
  • a filled hexagon of any color
  • two different unfilled hexagons with different corner shapes

Task until next session:

  • continue to work on the examples and experiment with them, so we can share
    and discuss our filled canvases next time.

Session 3 - HTML Canvas: animated and interactive

  1. Recap stage: sharing and discussion of canvas examples from last time
  2. Input stage: walk through the clock tutorial
  3. Workshop stage: recreate the clock from the clock tutorial

Exercise 1: the canvas clock

  • Customize the clock created in the clock tutorial, eg.:
    • make it more colorful
    • add a clock ticking sound (not actually canvas related)
    • add a function to stop and start the clock
  • If you already work on the exercise 2 hours or longer:
    • instead of finishing the exercise please just add a comment in your code,
      where you stopped, what you still wanted to do and why it did not work
      as expected (if time was not the only factor keeping you to finish the example)
  • Submit your code in the Exercise 1 folder in our base cloud share for this course.
    To do so, create a folder with your student id and put your code in it
  • Keep in mind that you get the full points also if you cannot complete it, but if
    you add the comment as described above after 2 hours working on the exercise.

Session 4 - Jumping Block in Canvas

General outline:

  1. Recap from last session
  2. Jumping Block part 1 - setup, update loop and block drawing
  3. Jumping Block part 2 - adding controls & left/right movement
  4. Jumping Block part 3 - adding jump
  5. Jumping Block part 4 - adding ground, game boundaries and one obstacle

This time we will be more iterative and mix input and experimentation in
short cycles, along the 4 parts of creating our simple jumping block game
prototype.

Part 1 - setup, update loop and block drawing

Use the scaffold folder in course's base cloud folder and add a canvas to the HTML with a width of 800px and a height of 400px.

In the main.js then create the following:

  • a gameState object that can store the players x and y position, as well as a reference to the canvas and the drawing context
  • a drawPlayer function that receives the players position and the drawing context and then draws a filled rectangle as the player (suggested width and height: 40 pixels)
  • an update function that clears the canvas (e.g. using clearRect()), adds 10 pixels to the players x position and then draws the player calling the drawPlayer function.
  • an init function that receives a state parameter and uses it to initialise the game state, draws the player for the first time and sets up an interval of 1 second, that calls the update function with the game state as an argument

In the end you will need to call the init function, once the document is set up. Basically you could just add a call to init() somewhere in the main.js file, or in a separate script in the html that is loaded at the end. A good practice though is to check whether the document has been fully loaded and only then call the init function. To do that just put the following code at the end of your main.js (actually you could put it anywhere, but the end of the file seems to be a good place):

document.onreadystatechange = () => {
    if (document.readyState === 'complete') {
        init(gameState)
    }
}

Under the following link you will find a reference solution for this part:
https://tantemalkah.at/artful-coding/2022st/session-content/session-4/part1/

Part 2 - adding controls & left/right movement

Building on the last part we now want to add some controls to be able to move the player to the left and right. What you need to do for that is:

  • add a controls object to your gameState that stores whether the left, right or up keys are currently pressed
  • add a handleKeys function that receives an event and a state parameter and then checks whether any of the three buttons is currently pressed and sets the values in gameState.controls accordingly
  • add the handleKeys function as an event handler for the onkeydown and onkeyup events with the state as a second parameter. This could be done with the following code (using an arrow function so that the event handler gets access to the state object):
    ​​window.onkeydown = (event) => { handleKeys(event, state) }
    ​​window.onkeyup = (event) => { handleKeys(event, state) }
    
  • in the update function instead of the code that just adds 10 pixels to the players x position you should now check if the left or right controls are true and then either add or subtract a certain amount of pixels from the players x position. Even better would be to add a separate parameter for the players x velocity in the game state. Then we can set the velocity and just before we draw the player add whatever is set in the velocity to the actual x position.
  • finally you might want to speed up the interval, and instead of 1 second choose a fraction of a second, e.g. 1000/50 (which is 20 ms) to get a game/rendering speed of 50 fps.

Under the following link you will find a reference solution for this part:
https://tantemalkah.at/artful-coding/2022st/session-content/session-4/part2/

Part 3 - adding jump

Building on the last part, we now also want to be able to jump, when the up key is pressed.

Here we have to add a y velocity to the player, because we need some form of gravity that pulls the player down, once they start jumping up. Also the player should only be allowed to jump, if they are standing on the ground. If (in the last part) you already set the controls also for the up button, now you only need to add some code to the update function that calculates the y velocity and the y position on the player, before the player gets redrawn.

For a very simple gravity model we could just set the y velocity e.g. to -20 when the player starts jumping. The y position then is calculated similarly to the x position just by adding the current y velocity to the y position. In order for the player to not indefinitely jump upwards, we have to reduce the upwards velocity. Or in other terms, we have to add some gravitational pull. So whenever the player is not standing on the ground, we can just add 1 to the y velocity. As soon as the player reaches the ground again, we can set the y velocity to 0 again.

Under the following link you will find a reference solution for this part:
https://tantemalkah.at/artful-coding/2022st/session-content/session-4/part3/

Part 4 - adding ground, game boundaries and one obstacle

Wow, we can already run around and jump. This is quite nice. But for the final prototype we should also add game boundaries, so the player cannot run out of the screen. Also some visual ground would be nice. And to top all of that, one obstacle would be nice too.

So what you'll have to do is:

  • add a drawGround function, that just draws a line (or a long and thin rectangle) below where the player is standing, and call this function in your update loop
  • add some code to the update loop that checks whether the newly calculated x position is out of bounds and just set it to the most left or right position there is
  • finally add a drawObstacle function that draws a rectangle at a certain position in a different colour than the player. This too has to be called in the update loop. Additionally we have to add some code that checks whether the player is colliding with the obstacle and in that case not let them move further to the left/right, if the obstacle would be in the way.

Under the following link you will find a reference solution for this part:
https://tantemalkah.at/artful-coding/2022st/session-content/session-4/part4/

Exercise 2: recreate and improve the jumping block prototype

  • Make sure to have a prototype of your own, with all of the above functionality. Then play around with some of the parameters. E.g.:
    • try out different fps values for the update loop interval
    • try out different values for the x and y velocity and the gravity calculation
    • try out different canvas and object sizes
      If you feel up to it, try to improve the collision handling for the obstacle, so that the player could actually land on the obstacle and move left and right from there, maybe even jump from there. Or just try to add another obstacle and add collision handling for that as well.
  • Burnout-prevention shortcut (if needed): if you already work on the exercise for 2 hours or longer:
    • instead of finishing the exercise add a comment in your code, where you stopped, what you still wanted to do and why it did not work as expected
  • Submit your code in the Exercise 2 folder in our base cloud share for this course. To do so, create a folder with your student id and put your code in it
  • Keep in mind that you get the full points also if you cannot complete it, but if you add the comment as described above after 2 hours working on the exercise.

Session 5 - Live Server & Phaser init & Jumping Block in Phaser

General outline:

  1. Recap from last session
  2. Live Server
  3. Phaser init
  4. Jumping Block

Live Server

While our code so far (also in Artful Coding 1) could always be run by opening
the files directly from your hard drive with a browser, usually at some point
you might want to put your code onto a webserver. Additionally, for more complex
projects and in modern web development in general, we might want to use modules.

Modules are a neat way to structure our code into different files, and to import
only those parts, we need at a certain point in our application. The MDN Web Docs
has a guide on JavaScript modules
that explains why modules can be very helpful, and how they work.

The important thing is, that most browsers will - for security reasons - not load
modules when you open your HTML file directly from your harddrive.
We would have to actually put the code on a web server to make it work. This would
be of course tedious to do, everytime we change and test our code. That is what
development servers are good for.

There are many different options out there. And there are also a lot of guides out
there how to do that. For example the Getting Started with Phaser 3 Guide
talks about this right away in the intro section. If you already have a setup with
any developmen/live server, feel free to reuse this. Or try out those things mentioned
in the Phaser 3 Guide.

But if you are already using VSCode as your editor, the probably best and least
effort solution is to install the Live Server
extension by Ritwick Dey.

Phaser init

Next we will download the current stable Phaser 3 release in its minified version.
For more details on the Phaser 3 setup, follow the Getting Started guide linked
above. As a shortcut, you can go to https://phaser.io/download/stable and download
the linked phaser.min.js file. Put this into the assets folder of a fresh
project (based e.g. on our scaffold).

Then just add a <script src="assets/phaser.min.js"></script> right before you load
your own main.js file. In the main.js we can work with Phaser 3. For a start
we'll follow the Getting Started with Phaser 3 guide and create the Hello World
from there.

To start we have to create a game config by adding the following:

var config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    physics: {
        default: 'arcade',
        arcade: {
            gravity: {
                y: 200
            }
        }
    },
	scene: {
        preload: preload,
        create: create
    }
};

Then we have to create a new instance of a game with this config:

var game = new Phaser.Game(config);

Once we did this, we can use the preload and create functions
(configured as defaults for preloading and creating the first game
scene in the config.scene object):

function preload () {

}

function create () {

}

The preload function is used to load all game assets that will be
used in a scene, for example images, sounds, etc. Everything that
should be available right away when we later render it in the game.

For this Hello World three images will be loaded from the Phase Labs
website. So let's put the following into our preload function.

    this.load.setBaseURL('http://labs.phaser.io');
    this.load.image('sky', 'assets/skies/space3.png');
    this.load.image('logo', 'assets/sprites/phaser3-logo.png');
    this.load.image('red', 'assets/particles/red.png');

This does not change anything yet, it just makes sure that we can
use those images by using the strings 'sky', 'logo' and 'red'
later on in our create function.

To actually create some things happening on the screen we now put
the following code into the create function:

    this.add.image(400, 300, 'sky');

    var particles = this.add.particles('red');

    var emitter = particles.createEmitter({
        speed: 100,
        scale: { start: 1, end: 0 },
        blendMode: 'ADD'
    });

    var logo = this.physics.add.image(400, 100, 'logo');
    logo.setVelocity(100, 200);
    logo.setBounce(1, 1);
    logo.setCollideWorldBounds(true);

    emitter.startFollow(logo);

You should now see a Phaser 3 logo moving around the sky,
emitting pinkish red particles.

Jumping Block

Now that we can use the code from above as our first Phaser 3 boilerplate.
Just remove everything again from the preload and the create functions,
so that they are empty. The we will also add an update function similar
to the other two. And we also have to add it to our scene config, which
then should read:

	scene: {
        preload: preload,
        create: create,
		update: update,
    }

One more little thing you might want to do is to explicitly place the
canvas in your page, so that you can also center it.

In our course scaffold folder we could just add the canvas between the
header and footer in the index.html like this:

    <div class="centered">
      <canvas id="game-canvas"></canvas>
    </div>

Then in the main.js we have to add a canvas option to our game config,
telling phaser which HTML element to use as the cavas, as well as explicitly
set the render type to the canvas engine:

    type: Phaser.CANVAS,
    canvas: document.getElementById('game-canvas'),

Now we are ready to implement our Jumping Block prototype in Phaser.

First, let's add some of the game objects in the create function.

As our ground, obstacle and player all are just rectangles of a
different color, we can easily add them to the scene like this:

function create () {
    // add the ground
    this.add.rectangle(0,300, 800,5, 0xCCCCCC).setOrigin(0,0)
    // add the player
    this.add.rectangle(100,200, 40,40, 0xff00ff).setOrigin(0, 0)
    // add the obstacle
    this.add.rectangle(200,230, 30,70, 0x00aa00).setOrigin(0,0)
}

Now we already see the first static image of the scene. But it does
not do anything. Now we have to add all the movement and collission
magic to make our scene dynamic. While this was quite tedious in our
Canvas Jumping Block example last session, Phaser with its physics
model can do a lot of the tedious work for us.

Before we do that, one important thing to keep in mind: we are adding
the Game objects here by using this. While we just defined a simple
function, outside of any object, the function will be called from
the Scene object, as we configured it in our game config. This is
why this in context of the preload, create, and update function
always refers to the current scene. This way we can also store our
own objects (e.g. a reference to our player) on the scene, e.g.
by setting this.player.

Now, lets continue, by creating a group of static physics objects,
which we'll call ground. And then add our actual ground and the
obstacle to it.

function create () {
    // create a static physics object group to fill with our non-movable objects
    const ground = this.physics.add.staticGroup()

	// add the ground
    const ground_rect = this.add.rectangle(0,300, 800,5, 0xCCCCCC).setOrigin(0,0)
    ground.add(ground_rect)

    // add the player
    this.add.rectangle(100,200, 40,40, 0xff00ff).setOrigin(0, 0)

    // add the obstacle
    const obstacle1 = this.add.rectangle(200,230, 30,70, 0x00aa00).setOrigin(0,0)
    ground.add(obstacle1)
}

So the only thing we did is to use this.phyics.add.staticGroup() to
create a statics physics group, store it in a ground variable, and
then use its ground.add() function to add the rectangles we have
already drawn for the ground and the obstacle.

Not that here we used const ground, as we will not need the ground
elsewhere later. But in case we would want to use it also in our
update function we should rather store it on the Scene object,
e.g. as this.ground. You'll see in a minute why this is important
when we create the player's dynamic physics object and the movement
controls.

For now nothing really changed. We still see the same thing as before.
Only now Phaser knows that that the ground and obstacle rectangles
are not just two simple drawings on the canvas, but actual physical
objects that will not move. So we can later do stuff with it.

But first, let's create an actual physics object for our player.
Instead of the standalone this.add.rectangle line for our player
we now write it like this:

    // add the player
    const player_rect = this.add.rectangle(100,200, 40,40, 0xff00ff).setOrigin(0, 0)
    this.player = this.physics.add.existing(player_rect)

So we call this.physics.add.existing() to create a new dynamic physics
object, as we did use this.physics.add.staticGroup() to create an
empty group for static physics object. A difference her ist, that
we use the group to add objects afterwards. With the add.existing
we can take a GameObject we already create (our player rectangle)
and make a dynamic physics object out of it. That is why we store
it as this.player, so we can later access it in our update function.

Take look. Now we will only see our player shortly, because as soon
as we load the page and the game starts, the player starts falling.
Well, because it is a dynamic physics object, and we have a physics
set with a gravity. And gravity usually pulls down objects towards
the bottom.

But, you may wonder, what do we have our ground for? This is a good
question, because this is exactly what we created our ground for,
as a static physics object group. So first of all the ground does
not fall down, because it is static and therefore not affected by
gravity. Secondly, we now want to use the ground to stop the player
falling. We can do that by telling Phaser that those two things,
our player, and our static objects group called ground, can actually
collide.

We only need to add a collider to the physics of our scene:

    // add a collider between ground and player
    this.physics.add.collider(ground, this.player)

Awesome, so the player starts falling, but stops on the ground.
And this is also the reason why, in our little prototype, ground
was created as local variable in the create function, and not
stored on the scene object itself. Because all we set up, is
done in the create. Phaser then keeps track of everything. But
imagine you add some features to the game, so that another
obstacle could suddenly show up, after the player jumped over
the first obstacle. We would have to check for that in our
update function. Then it would be handy to have a quick reference
to the ground again.

Now, it would be nice if we could actually move the player. So
let's add some controls. Remember how tedious it was in the last
session when we had to do that manually?. The proces is somewhat
similar in Phaser, only it is soooo much less tedious.

In our create function we just need to create an object that
keeps track of the keys we want to use:

    // add a cursors object that keeps track of our cursor key status    
    this.cursors = this.input.keyboard.createCursorKeys()

That is all. No tedious writing of event handlers. Phaser handles
if for us. Now in our update function we can check whether the
left, right or up arrows are pressed and instruct the player to
move.

Let's start with left/right movement:

function update () {
    if (this.cursors.left.isDown) {
        this.player.body.setVelocityX(-200)
    } else if (this.cursors.right.isDown) {
        this.player.body.setVelocityX(200)
    } else {
        this.player.body.setVelocityX(0)
    }
}

Whenever the left button is pressed down, we set a negativ x velocity
to the player, so the player object will automatically start to move
to the left. In case left is not pressed, but right, we set a positive
x velocity, so the player moves right. And then we also need to check
if non of those two buttons is pressed, in which case the player should
stop again, so we set the velocity to 0.

Wonderful, now we can run left and right, and we can already bump into
our obstacle, which will stop as. Because we added it to the ground
objects group, which is configured to collide with the player. Remember,
how tedious the collission handling was in the last session?

But there is a problem. We can run out of the frame on the left. And we
can't even return. Well, because the ground really only goes to the
left side of the frame (x-position: 0). If you go beyond that, you'll
just fall, because there is no ground, but there is gravity all around.
Try it out, if you go left, and quickly go right again after you are
outside the frame, you will see the player falling down below the ground.

So we have to add another collission check. We coul create another
obstacle, that is just outside the visible area, and add it to the ground.
But there is an even easier way to do that. Because dynamic physics objects
have a function setCollideWorldBounds(), which we can call to set whether
the object collides with the visible bounds of our scene. We can do
that in the create function just after creating the player, by adding:

    this.player.body.setCollideWorldBounds(true)

Note that this function is defined on the player's body, that was added
to the rectangle after we created with add.existing. You might come
accross many examples where this is done directly on the player or
other physics object. We'll see examples in the next session. This
has to do with how the physics object was created. For now it is just
important to keep in mind that physics objects always have a Body
(which is its own class in the Phaser physics system), and this is
used to handle all collissions, movements, etc.: physics stuff, you
need a body for.

Ok, so, now we are only missing the capability to jump, in order to
also cross the obstacle. There is not a lot left to do now. We only
have to check whether the up key is pressed (AND if the player is
standing on the ground), and then set a negativ y velocity:

    if (this.cursors.space.isDown && this.player.body.touching.down) {
        this.player.body.setVelocityY(-200)
    }

Wow, that's it. Our game is done. And we can even land on the
obstacle and jump again from there.

Now you can also play around with the values for gravity and
velocities, to find your preferred jump height and velocity.

And why did we check for the player touching the ground?
Well, try it out and fly to get high.

If you are puzzled by what other things all of those game objects
could to, take a look at https://phaser.io/learn
There you'll find, among other things, the
Making your first Game
guide as well as the API Docs. While
the API Docs might be quite intimidating at first, they can be helpful
when you already know a few things but just can't remember how the
parameters are used, or how it is actually called. Try to go there,
and click on the Game Objects
section, just to get a peek at what different game objects there are.
There you already see that you could use a lot of different things
instead of our boring rectangles. Or just try to search for a term,
e.g. "body" and you will quickly get to the page for the
Phaser.Physics.Arcade.Body,
on which you can see what other properties and methods your player body has.

Under the following link you will find a reference solution for this session:
https://tantemalkah.at/artful-coding/2022st/session-content/session-5/

Exercise 3: recreate and improve the jumping block prototype

  • Make sure to have a prototype of your own, with all of the above functionality.
    Then play around with some of the parameters. E.g.:
    • try out different values for gravity and velocity
    • try out different canvas and object sizes
      Then add one or more other obstacles. If you feel playful, try to add obstacles
      in way so that the player could actually jump from obstacle to obstacle until
      they reach a higher ground.
  • If you already worked on this exercise for 2 hours or longer:
    • instead of finishing the exercise add a comment in your code,
      where you stopped, what you still wanted to do and why it did not work
      as expected
  • Submit your code in the Exercise 3 folder in our base cloud share for this course.
    To do so, create a folder with your student id and put your code in it
  • Keep in mind that you get the full points also if you cannot complete it, but if
    you add the comment as described above after 2 hours working on the exercise.

Session 6 - reflection & workshop session

This session is used to reflect on what we have done so far, and elaborate
on particular topics the participants want to know more about, or are struggling
with.

It is also used as a common working session to finish and experiment with
the recent exercises.

Session 7 & 8 - website mini game

In these two final sessions we will recreate the mini game featured on the
Artful Coding 2 website.

The idea of the mini game is that the player can run around and jump to collect
the randomly distributed letters, which are contained in the the title
"Artful Coding 2". To make it a bit more interesting, the there are some
obstacles (rocks on the ground) the player has to jump over. Also the ground ends after some time walking
to the left and right. If the player falls down, they will respawn centered
above the ground again. Every letter that is collected will the be highlighted
in the pages main heading, which is initially set to 10% opacity.

To achieve this we we will use:

  • a simple rectangle for the ground, as basis for a static physics group
  • some images for rocks which can be added to the ground
  • a spritesheet for the player and to create walking animation from
  • several text objects to place collectible letters around the scene
  • a function to handle collissions between the player and the letters,
    which removes the letter from the scene, but highlights the letter on
    the web page (as an example how the Phaser based game can interact with
    just anything else on the website in which it is integrated)

Before we start: a new setup

Previously we created the Jumping Block in Phaser simply in
one single main.js file and without any sophisticated help by our
IDE. But for this session we'll create a more modern, and especially more
scalable setup. Because at some point, having everything in one
main.js file, without structuring things into different classes and
objects, will become immensely unwieldy. It will also hamper your
development progress.

Let's start by creating a new scaffold folder for Phaser games. We could
simply call it scaffold-phaser. In it we need:

  • an assets folder containing:
  • a src folder containing (for now):
    • an empty folder lib
    • an empty folder types
  • an index.html file, containing our game canvas - more on that later
  • a style.css that gets included by the index.html
  • a jsconfig.json file containing the following configuration:
    ​​{
    ​​    "compilerOptions": {
    ​​        "module": "es6",
    ​​        "target": "es6"
    ​​    }
    ​​}
    
    This is used to tell VS Code that it should treat our code as modularized
    ES6 (ECMAScript 2015) code. Combined with the Phaser type definitions we
    will also add in a few minutes, VS Code then is able to provide us a lot of
    help when writing code using all the different Phaser components.

The content of the index.html could look something like the following:

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="styles.css">
    <title></title>
  </head>
  <body>
    <header class="centered">
      header-content
    </header>

    <section class="centered">
      <canvas id="game-canvas">
        Your browser does not seem to support the HTML 5 Canvas element, sorry.
      </canvas>
    </section>

    <footer class="centered">
      footer-content
    </footer>

    <script src="assets/phaser.min.js"></script>
    <script type="module" src="src/main.js"></script>
  </body>
</html>

Important: note the subtle difference in loading the main.js file here
vs. how we did it so far. Here we use a type="module" attribute in the script
tag, to tell the browser, that this is not just a single Javascript file
to load and execute, but a module that can use import statments to load stuff
from other modules and export to provide stuff to other modules.

The style.css in this scaffold then does not contain anything other then
a rule for the centered class to center our header, footer and game canvas:

.centered {
    margin: auto;
    width: fit-content;
}

Now, for that to work, we also need the actual src/main.js file, in which
we create a new Phaser game with a basic configuration:

import Phaser from './lib/phaser.js'
// import Scene from './Scene.js'

export default new Phaser.Game({
    type: Phaser.CANVAS,
    width: 1000,
    height: 280,
    canvas: document.getElementById('game-canvas'),
    // scene: [Scene],
    physics: {
        default: 'arcade',
        arcade: {
            gravity: {
                y: 600,
            },
            //debug: true,
        }
    }
})

The only thing still missing for this to be functional is the src/lib/phaser.js file.
You see in the code above, that we are importing the Phaser class from it. But
how does that work? The only thing we have to out into the src/lib/phaser.js file is:

export default window.Phaser

This is, because our setup now facilitates ES6 modules. But Phaser itself was just
included as an unmodularised script tag in the index.html, just before our main.js
was included as a module. So Phaser already exists on the window object, but we
want to be able to import it consistently everywhere in our modules. That is what
the src/lib/phaser.js does: it takes window.Phaser and exports it as a default.
So we can use import Phaser from './lib/phaser.js' in our main.js (and other modules),
to use the Phaser class.

Once this is done, the (empty) game should load, and the browser console should
tell you that Phaser (and which version of it) is loaded.

Now the only convenience thing still missing to guide us when writing code, are
the type definitions for Phaser.

These are files provided by Phaser, which help your IDE (e.g. VS Code), to
provide you with more context info when coding. They can be found in the
types folder of the official Phaser repository.

To use those type definitions with the IntelliSense
feature in VS Code, we need to get the phaser.d.ts file from there and put it into the
src/types folder.

Once you have that set up, you can type Phaser. in your code (e.g. in the main.js)
and VS Code will provide you with a lot of things you could do with the Phaser object.

You can find a reference scaffold-phaser folder, also packed into a .zip or .tgz file,
on the website in session-content/session-6.

Part 1 - Making a scene!

Now, before you change any code, create a copy from your scaffold folder, so you
can later reuse it.

In the previous Jumping Block prototype in Phaser our game config contained the
following:

const config = {
	// ...
	scene: {
        preload: preload,
        create: create,
        update: update,
    }
}

This implicitly set up the game with a single scene, linking the preload,
create and update functions of that scene to the three similarly named
functions defined in the main.js.

Now we still will only need one scene, but we want to go big about that!
Also, this is already a good preparation if we want to add additional scenes.
E.g. we might want to add an intro scene for the game, or a scene for displaying
an outro, credits, or a success / game over screen.

This is why we'll modify our game config and remove the three comments that
have been set in the src/main.js of the scaffold folder. It now should look
like this:

import Phaser from './lib/phaser.js'
import Scene from './Scene.js'

export default new Phaser.Game({
    type: Phaser.CANVAS,
    width: 1000,
    height: 280,
    canvas: document.getElementById('game-canvas'),
    scene: [Scene],
    physics: {
        default: 'arcade',
        arcade: {
            gravity: {
                y: 600,
            },
            debug: true,
        }
    }
})

So ourscene configuration is now just a list with one scene class: [Scene]. And
this Scene is loaded from the Scene.js file, so we'll have to create it. Note,
that I also removed the comment in the physics config for the debug: true. Having
set debug to true will help a lot throughout development to see how physics objects
behave and interact. But don't forget to turn it back to false (or comment this line)
in your finished game.

But now, let's create the src/Scene.js file from which Scene gets imported:

import Phaser from './lib/phaser.js'


export default class Scene extends Phaser.Scene {
    constructor () {
        super('scene')
    }

    init () {
    }

    preload () {
    }

    create () {
    }

    update () {
    }
}

Here we create a new class Scene which is derived from the Phaser.Scene class.
So it will do everything as a regular Phaser Scene. That is why in the constructor
we also call super('scene') to do all the initialisations Phaser has in place for
a standard Scene.

Then we add four methods to our Scene class: init, preload, create, and update.
Check out the Phaser API docs for Phaser.Scene.
It tells us that we

can also define the optional methods
init(),
preload(),
and create().

We can also see that the Phaser.Scene has only one method: update().
And it also tells us:

This method should be overridden by your own Scenes.

This method is called once per game step while the scene is running.

While we already kind of know what these methods do from our previous Jumping Block example,
it is always good to have the docs ready, in case we want to be sure. Also it tells us,
that init will be called before preload and create, and can be used to set up everything
that can be set up before we load our assets and create our game objects.

We will use the init later to set up the letters of the title, which will be highlighted
whenever the player collects the letters in the game.

For now we'll just load some assets and create a ground. To load the assets put the
following into the preload function:

    preload () {
        this.load.spritesheet(
            'player', 'assets/character_spritesheet.png',
            { frameWidth: 125, frameHeight: 125 },
        );
        this.load.image('rock1', 'assets/rock_50x50.png')
        this.load.image('rock2', 'assets/rock_75x50.png')
        this.load.image('rock3', 'assets/rock_50x75.png')
        this.load.image('rock4', 'assets/rock_75x75.png')
	}

To be able to load those files you have to create them first. Or use the ones in
the assets folder of the website.

Now we can use them to create the ground and some obstacles. This is done in the
create() method, but we'll also add an addRocks() method, which handles all the
obstacle creation:

    create () {
        this.ground = this.physics.add.staticGroup()
        let rect = this.add.rectangle(-500,260, 1500,10, 0xffffff).setOrigin(0, 0)
        this.ground.add(rect)
        this.addRocks()
    }

    addRocks () {
        this.ground.create(-700,240, 'rock2')
        this.ground.create(-100,240, 'rock1')
        this.ground.create(20,230, 'rock4')
        this.ground.create(700,240, 'rock1')
        this.ground.create(1200,230, 'rock3')
    }

While this is functionally enough and you should see the two most central
obstacles in your game canvas, we should also add the declaration for the
ground to the class definition. Add the following to the top of the
Scene class:

    /** @type {Phaser.Physics.Arcade.StaticGroup} */
    ground

This tells the class, that it contains a property ground. And the comment
tells VS Code, that the type of whatever ground will be initialised with
(later in the the create method) is a
Phaser.Physics.Arcade.StaticGroup.
With that, whenever we use e.g. this.ground.create() or this.ground.add() in our methods,
VS Code will provide us useful context information with what the expected parameters are
and what the method does.

That's it for part 1. We have a scene, containing a ground and some obstacles.
Under the following link you will find a reference solution for this part:
https://tantemalkah.at/artful-coding/2022st/session-content/session-6/part-1/

Part 2 - Adding a player

For this second part let's add a player to the scene.

We'll add a class member definition for the player and the cursor keys, which
will be used to control the player. So just after the definition of the ground
add the following:

    /** @type {Phaser.Physics.Arcade.Sprite} */
    player
    /** @type {Phaser.Types.Input.Keyboard.CursorKeys} */
    cursors    

Now in the create function, after we have created the ground, we create a
player sprite and add a collider between ground and player:

        this.player = this.physics.add.sprite(400,200, 'player')
        this.player.body.setSize(35)
        this.physics.add.collider(this.ground, this.player)

Now let's initialise our cursors and add an event handler for when the
up key or space is pressed:

        this.cursors = this.input.keyboard.createCursorKeys()
        this.cursors.up.onDown = (event) => { this.jump(event) }
        this.cursors.space.onDown = (event) => { this.jump(event) }

Of course we now also have to add a jump function to our class:

    jump(event) {
        if (this.player.body.touching.down) {
            this.player.setVelocityY(-400)
        }
    }

To add some left and right movement we will use our tried and tested
approach of checking the keys in the update function. But we'll add three
helper functions to our class first: walkLeft, walkRight and walkStop:

    walkLeft(event) {
        this.player.setVelocityX(-200)
    }

    walkRight(event) {
        this.player.setVelocityX(200)
    }

    walkStop(event) {
        if (this.cursors.left.isDown || this.cursors.right.isDown) return
        this.player.setVelocityX(0)
    }

Now the update function can be quite succinct (and later we cann add controls
for the player animation in the walk functions):

    update () {
        if (this.cursors.left.isDown) {
            this.walkLeft()
        } else if (this.cursors.right.isDown) {
            this.walkRight()
        } else  {
            this.walkStop()
        }
    }

Now we can run left and right and jump. We can also run and jump out of the
screen. But in this case the player should be able to walk further than that.
Only we would like to follow them. For that we can use the Scene.cameras
which Phaser provides for every scene. We only have to tell the main camera
to follow the player object. The only thing we need to do for that is to
add the following to lines in our create function:

        this.cameras.main.startFollow(this.player)
        this.cameras.main.setDeadzone(200, 100)

Now we can move and the camera will follow the player, if they leave the
center by a defined amount (200, 100). Check the docs of the
setDeadzone
function and play around with the values if you want.

Another cool thing we can do with cameras: Zoom! While this is not needed for
the game, it might help us while developing it. E.g. to place obastacles and
later all the letters, it might be nice to see a bigger part of the scene.
So let's add a zoom function which we can use to zoom out and in, by pressing
the Shift key - but only if debug is enabled. In the create we'll just add
one more event handler, similar to jump:

        if (this.game.config.physics.arcade.debug) {
            this.cursors.shift.onDown = (event) => { this.zoom(event) }
        }

Of course we also need to add the zoom function now to our Scene class:

    zoom(event) {
        if (this.cameras.main.zoomX === 1) {
            this.cameras.main.setZoom(0.3, 0.3)
        } else {
            this.cameras.main.setZoom(1, 1)
        }
    }

What the function does is to check whether the main cameras zoom is set to 1,
and in that case set a new zoom level to 0.3 in x and y direction. Otherwise
(if it is not 1, it will be 0.3), it will be set back to 1 in x and y direction.

Now we can use the Shift key to see the whole scene. Play around with the values
in the setZoom function, to see how it works. And check the documentation for the
setZoom function,
if you want to know more how that works.

That's it for part 2. Now we can run and jump around the scene with our player.
Under the following link you will find a reference solution for this part:
https://tantemalkah.at/artful-coding/2022st/session-content/session-6/part-2/

Part 3 - Animating and respawning the player

For this part we will use our spritesheet to create walking animations, so
that the character will not move around like a very stiff stick figure, but
a more animated one.

For this we have to first create some animations by telling the framework
which frames from the spritesheet to use. So let's put the following code
somewhere into our create() function, e.g. at the beginning:

        this.anims.create({
            key: 'walk-right',
            frames: this.anims.generateFrameNumbers('player', {start: 1, end: 4}),
            frameRate: 7,
            repeat: -1,
        })
        this.anims.create({
            key: 'walk-left',
            frames: this.anims.generateFrameNumbers('player', {start: 9, end: 6}),
            frameRate: 7,
            repeat: -1,
        })

To get into the details on what is happening here check out the docs for the
create() function of the AnimationManager
and the Animation config object.
Basically we are just setting up two animation cycles that we label walk-right
and walk-left, so we can easily apply it later to some GameObject - in this
case we will want to apply it to the player. The frameRate is used to set
the speed of the animation, the repeat parameter can be used to define how
often the animation should repeat (-1 is used here to repeat infinitely), and
the frames just define the single images/frames in the animation. This is
an array of AnimationFrame
objects, which we could set up manually. But there are different ways to easily
create the animation frames. We just use the AnimationManager's
generateFrameNumbers()
function, to generate this for us, based on the player sprite we loaded
in our preload() function.

So, once this animation is set up, we can use it. And we want to start the
animation whenever the player starts to move, and stop it when the player
stops. As we have been clever before, we already have some functions to
walkLeft, walkRight, and walkStop. So we'll just add some lines there to
start and stop the animation. The adapted code then should look like this:

    walkLeft(event) {
        this.player.setVelocityX(-200)
        this.player.anims.play('walk-left', true)
    }

    walkRight(event) {
        this.player.setVelocityX(200)
        this.player.anims.play('walk-right', true)
    }

    walkStop(event) {
        if (this.cursors.left.isDown || this.cursors.right.isDown) return
        this.player.setVelocityX(0)
        this.player.anims.stop()
        this.player.setFrame(0)
    }

So we just added one line with a call to the AnimationManager's play() function
in the functions where we start walking. And a similar line with the corresponding
stop() function in walkStop(). Additionally we use the setFrame() function to
reset the animation state to the standing-still image for the player.

But try to play around with it. What happens, when you remove the stop or setFrame
functions again?

And of course this is just a simple, first version of walking animation. But it
already looks so much more appealing than the stiffly unanimated stick figure
from before.

So we are almost done for this part. The only thing we'll still tackle is the case
when the player falls down. So far, you'll just keep falling down, and the game
is basically over without telling you.

In our case we want the player to just fall a little bit, and then respawn above
the platform again, so they can keep collecting the letters, which we'll add in
the next and final part.

The only thing we really need to do for this, is add a little condition in the
update() function that just checks if the player is below a certain point
(or in corrdinates: above a certain y value). In that case we'll just reset
the player's y postion. And we'll also reset the x position, so that they
are on the center of the platform again:

        // when player jumps off the ground, they respawn above again
        if (this.player.y > 1000) {
            this.player.setX(400)
            this.player.setY(0)
            this.player.setVelocityY(0)
        }

You'll notice that we also set the players y velocity back to 0. Try it
out without it. And also play around with the y values. It seems a bit
smoother this way, but maybe you find even better settings.

That was it for part 3.
Under the following link you will find a reference solution for this part:
https://tantemalkah.at/artful-coding/2022st/session-content/session-6/part-3/

Part 4 - Create collectible letters

Now for the final part of this little game prototype we want to add some
letters to the scene, which can be collected to light up corresponding
letters in the HTML page.

For that, first of all, we need some content on the HTML page. So let's put
two headings into our <header>, and give it an id of title and subtitle:

      <h1 id="title">Artful Coding</h1>
      <h2 id="subtitle">Web-based games development <span id="letter-2">2</span></h2>

You will notice, that I put the 2 inside a <span> tag. This is for two
reasons: first, this should be styled differently than the rest of the heading.
Second, we need some container for the letter, that we can address with an
ID. Because as soon as the player will collect the 2 in the game, we want
to light up this letter too.

Now, the same would actually also be needed for every letter in the "Artful Coding"
title. And I could do that just here, manually, in the HTML. To put a <span> with
some unique ID around every letter. But this would be quite tedious, and there is
a better, procedural way to do it: with a loop in our init() function. We'll
get to that in a minute.

First let's also add some styling to our styles.css, so that the 2 (and the spanned
letters we'll add shortly) are displayed with an opacity of 10%, a centered title, and
a white font on black background:

body {
    background-color: black;
    color: white;
}

#title, #subtitle {
    text-align: center;
}
#title span,
#subtitle span {
    opacity: .1;
}

That already looks a little more like on the artful coding web page. Now let's create
the <span>s for the single letters in the title:

    init () {
        const elTitle = document.getElementById('title')
        let titleHTML = ''
        for (const l of 'Artful Coding') {
            if (l === ' ') {
                titleHTML += ' '
            } else {
                titleHTML += `<span id="letter-${l}">${l}</span>`
            }
        }
        elTitle.innerHTML = titleHTML
    }

Here we are just addressing our HTML element as usual. Then we create a string
titleHTML, which starts out empty. In a loop through every letter of "Artful Coding"
(this includes the space), we'll add something to the titleHTML string. If it is
the space itself, just a space should be added. But if it is a letter, we'll add
a whole span tag, with an ID of "letter-${l}" and the letter itself inside it.
If you are unfamiliar with this syntax to create strings with dynamic content,
check out the MDN docs on template literals.
It would be similar to add a string like this: '<span id="' + l + '">' + l + '</span>'.
But template literals are a really neat way to contruct strings with content from
your variables.

So once we contructed the whole string, we just set it as the .innerHTML of our
element. Now also the letters in the title should be displayed with 10% opacity.

Next, we have to add letters to our scene. We'll do that in our create() function
like everything else, and we'll need to use the Text
GameObject from Phaser. But as our letters will have some unique properties, and
we have a modular setup already, we'll create our own GameObject.

For that we create a separate file Letter.js, next to our Scene.js and the
main.js, and put the following in it:

import Phaser from './lib/phaser.js'

export default class Letter extends Phaser.GameObjects.Text {
    /**
     * 
     * @param {Phaser.Scene} scene 
     * @param {number} x 
     * @param {number} y 
     * @param {string} text 
     */
    constructor (scene, x, y, text) {
        const r = Phaser.Math.RND.integerInRange(128, 255)
        const g = Phaser.Math.RND.integerInRange(128, 255)
        const b = Phaser.Math.RND.integerInRange(128, 255)
        /** @type Phaser.Types.GameObjects.Text.TextStyle */
        const style = {
            fontFamily: 'monospace',
            fontSize: '48px',
            color: `rgb(${r}, ${g}, ${b})`,
        }
        super(scene, x, y, text, style)
    }
}

What you see here, could also be called a wrapper around the standard
Phaser.GameObject.Text. The first part of the magic lies in the definition:
export default class Letter extends Phaser.GameObjects.Text. This is similar to
how we set up our Scene. So we are creating a class Letter, which
extends Phaser.GameObjects.Text. This means, that Letter has all the porperties
and methods Text already has - only that we can then add or change specific things.
And then we export default this class, so that it can be imported in our Scene
(and potentially every other Scene).

The comment block after the definition then is not necessary functionally, but
it tells VS Code (and other tools) how to interpret the parameters of our
contructor. This is helpfull wenn we use the Letter somewhere else, because
VS Code can provide some context info as help.

The second part of the magic then is our constructor (scene, x, y, text), which
will be used to construct a new Text object with a specific style.
In it we use Phasers random number generate to create three random RGB colour
values. Then we create a style object, where we instruct the Text to
use a monospace font of 48px, with our random RGB value. Then we call
the constructor of our parent class with super(scene, x, y, text, style).
That is similar if we would create a new Text
object.

Now this Letter class can be used to create Text objects, without the
need to provide a style configuration for every letter, and with every letter
getting a random colour.

So let's use this letter in our Scene. First we need to import it in the top
of our Scene.js file:

import Letter from './Letter.js'

Then we will also add a property letters to our class definition (just after
where we also defined ground, player and cursors). Similar to ground it
will be a static physics object group:

    /** @type {Phaser.Physics.Arcade.StaticGroup} */
    letters

In our create() function, just after we create the player, we'll add some code
to initialise the letters group, and tell it that it will contain objects created
from the class Letter. We'll also call an addLetters function, which should handle
our letter creation code.

        this.letters = this.physics.add.staticGroup({classType: Letter})
        this.addLetters()

Of course we have to add the addLetters method now to our class (similar to addRocks).
In a first version let's just try to place a single letter, to see how it works:

    addLetters() {
        const l = new Letter(this, 400, 50, 'A')
        this.add.existing(l)
        this.letters.add(l)
    }

You should see a single letter "A" now, a bit above the player. Now, why did we have
to add it two times? Try playing around with this code and comment out on of the
two add functions and see what it does.

The this.add.existing() function is responsible to add a GameObject to this scene,
so that it will be displayed. Because only creating the Letter does not do a lot. The
letter afterwards will exist in the whole games Phaser universe, but is not in any
way related to the scene. The this.letters.add() then is used only to add the letter
also to the static physics group. This does not matter very much at the current point.
You will only see (in debug mode), that a blue border will show up around the letter.
But we need this later, because we want to check collissions / overlaps between
letters and the player.

Good, now, we have one letter in the scene. We now could continue to use these three
lines over and over again, to place all other letters. If it would be important to us,
that every letter is placed at a very specific, carefully designed position, then that
would be a good option.

Also, to get a feel for it, try out to add some other letters manually, before you
continue.

But in the end we just want to have the letters (somewhat) randomly distributed
accross the scene. So, again, we'll use a procedural way to do it, and modify the
code of the addLetters() function as follows (I've included lots of comments to
walk you through this process):

    addLetters() {
        // first we just want to randomise the order of the letters how they
        // should appear in the game
        let sortedLetters = []
        for (const l of 'ArtfulCoding2') { sortedLetters.push(l) }
        let randomLetters = []
        // for every item in our sortedLetters array we will now take out a
        // random one and add it to the randomLetters array, as long as there
        // is something left to take out
        while (sortedLetters.length > 0) {
            const index = Phaser.Math.RND.integerInRange(0, sortedLetters.length-1)
            randomLetters.push(sortedLetters.splice(index, 1))
        }

        // now we create a slightly random first x position on the very left
        // of our scene
        let x = Phaser.Math.RND.integerInRange(-1000, -900)
        // and for every letter we will now create an actual Letter object
        // at another (slightly) randomised postion from the left of the scene
        // to the right
        for (const letter of randomLetters) {
            // for every letter just add something between 150 and 200 pixels
            x += Phaser.Math.RND.integerInRange(150, 200)
            // the y should always be between 0 and 50 (a bit above the player)
            const y = Phaser.Math.RND.integerInRange(0, 50)
            // now use this x and y to create a new Letter object for the letter
            const l = new Letter(this, x,y, letter)
            // add it to the scene explicityl, so that it shows up
            this.add.existing(l)
            // add it to our static physics group, so we can later handle
            // collisions / overlaps with the player
            this.letters.add(l)
        }
	}

Now you should have a scene filled with all the letters from the title,
plus the 2. And every time you reload the scene, the letters will be arranged
differently and have different colours.

Now, still in debug mode and having built in our nice zoom function, is a good
moment to test the zoom. Press Shift to see the whole scene and the distribution
of letters. If you don't like it, play around with the values in the addLetters
function.

The only thing still missing now is some function that gets called whenever the
player collides - or in this case overlaps - with the letters. This function then
should handle the removal of the letter from the scene, and the highlighting of
the corresponding letter in the heading.

While we already use a collider between the player and the ground, in this case
we don't want the player to actually collide with the letters. Because the letters
should not block the players jump. Instead of using a collider and a collision handler,
we'll add an overlap handler, just after we called the this.addLetters() method
in our create() method:

        this.physics.add.overlap(
            this.player,
            this.letters,
            this.collectLetter,
            undefined,
            this,
        )

This defines the player and the letters group as two things Phaser should check
for overlaps. And as soon as an overlap happens the this.collectLetter method
should be calle (which we still have to create). The undefined as the fourth
parameter tells Phaser not to start a processCallback function, because we
already have a collideCallback function. And the fifth parameter is the
callbackContext, which should be our scene, so this. Check the docs for the Phaser Arcade
Factory.overlap
function, if you want to know more how that works.

Now, whenever there is an overlap (or a collission, but without blocking the players
path), the collectLetter method of our scene will be called. So let's add this:

    collectLetter(player, letter) {
        // remove the letter from the scene and disable the its physics body
        this.letters.killAndHide(letter)
        this.physics.world.disableBody(letter.body)
        // now apply the letters colour to the corresponding letter in the
        // header, and set its opacity to 1
        const element = document.getElementById('letter-'+letter.text)
        element.style.color = letter.style.color
        element.style.opacity = 1
    }

The collectLetter method will always get two parameters, when it is called by Phasers
event handling system: the two objects that are colliding/overlapping. And as we
defined it with the player first and the letters second, when we added the overlap,
here we'll have the same order. In this case we just don't do anything with the
player, because we only remove the letter object from the scene, and then highlight
the correspoding letter in the heading.

So, that's it. The game is done. You can now happily collect letters.

Or refine it and add some more obstacles, create your own assets and animations,
use different texts, add sounds, or whatever else comes to your mind.

And keep in mind that you can use the zoom option with Shift, when you work on the
scene. But also keep in mind to turn debug: false in the game config, once you are
done and want to release the game.

Under the following link you will find a reference solution for this final part:
https://tantemalkah.at/artful-coding/2022st/session-content/session-6/part-4/

Happy hacking and happy letter collecting!

Exercise 4: recreate and improve the letter collector prototype

  • Make sure to have a prototype of your own, with all of the above functionality.
    Then play around with some of the parameters. E.g.:
    • try out different ways of placing the letters
    • try out different or additional placements for obstacles
    • create your own assets
  • If you already worked on this exercise for 2 hours or longer:
    • instead of finishing the exercise add a comment in your code,
      where you stopped, what you still wanted to do and why it did not work
      as expected
  • Submit your code in the Exercise 4 folder in our base cloud share for this course.
    To do so, create a folder with your student id and put your code in it
  • Keep in mind that you get the full points also if you cannot complete it, but if
    you add the comment as described above after 2 hours working on the exercise.

Session 9 - Brainstorming game ideas and team formation

Disclaimer: this session is tailored towards 2 participants, so you would need
more time and probably less iterations in case of a bigger class

10min:
We start with a short recap on what we did so far (in Artful Coding 1 & 2). The
aim of this session now is to switch away from learing and refining our coding
skills to ideation and game creation.

5min:
To start with, everyone familiarizes themselves with out collab board on tryeraser.com
and doodles around in the designated "Doodle Area".

5min:
The we start a one minute exercise: "Draw a (perfect, of course) dog!"
Afterwards we share our drawings and tell a little bit about our dog's character.
(This is mainly to loosen up and to shift our minds away from trying to be perfect)

30min (3x(5+5)):
Now a first ideation sequence starts, where we'll generate three different game
ideas based on the contraints we get from a game jam idea generator:
https://steven-the-gamer.itch.io/game-jam-idea-generator

With one set of contraints everyone gets 5 minutes, to create one game idea.
Then we'll share those ideas, before we create new game contraints with the
generator. This repeats until everyone created 3 different game ideas.

10min:
Here we'll take a first little break, which participants can also use to think
about whether they want to take up one of the game ideas to develop further,
or if they already an idea of their own which they would like to implement
within the remainder of this course.

20min (5+15):
Intro to this sequence, where everyone gets 15 minutes to refine one of the
game ideas. Then the 15 min counter starts in which the participants extend one
of the game ideas by describing:

  • the goal of the game
  • the rule(s)
  • feedback mechanism(s)
  • optionally:
    • what is needed to implement this idea? are there any specific requirements?

20min:
Sharing of ideas and discussion

Session 10 - Concept refinement & first implementation steps

This session is intended as a workshop session, where participants continue on refining
their game concepts from the last session. The focus lies on developing the concept into
a concrete description of a minimal version of the game and to outline a first approach
to implementation.

Session 11 - Implementation ctd., project discussion, closing review


This page is part of the course website at https://tantemalkah.at/artful-coding/2022st
All contents, where not otherwise noted, are licensed by Andrea Ida Malkah Klaura under a CC-BY-SA 4.0 license.