diff --git a/about.html b/about.html index f9f5189..e74385f 100644 --- a/about.html +++ b/about.html @@ -27,6 +27,7 @@ min-width: 80px; height: auto; border: 7px solid var(--topnavbuttonhover); + box-shadow: var(--shadow); } @@ -47,6 +48,7 @@
+
Hi there! I'm Joachim Ford, a solo developer @@ -56,6 +58,39 @@ or get in touch at hello@joachimford.uk. + +
@@ -77,8 +112,8 @@

Things I've worked on...

© Copyright joachimford.uk - - + + diff --git a/articles.html b/articles.html index 939f672..17a19ea 100644 --- a/articles.html +++ b/articles.html @@ -38,6 +38,16 @@

3 Tips for Game Development

+ + +

The Splash Dash Tutorial

+ + Making a playable JavaScript game in just over 1000 characters + is a difficult but motivating challenge. This article explains how + the game Splash Dash was made to fit within just 1024 bytes + +
+

Making games: The Bits That Nobody Notices

@@ -74,8 +84,8 @@

Procedural Room Generation - Snakes and Ladders

© Copyright joachimford.uk -
- + + \ No newline at end of file diff --git a/articles/3_tips_for_game_development.html b/articles/3_tips_for_game_development.html index 8411140..a474282 100644 --- a/articles/3_tips_for_game_development.html +++ b/articles/3_tips_for_game_development.html @@ -81,7 +81,7 @@

Step 2. Start Small

After messing around a bit with Pygame Zero, we decided to upgrade to Pygame, as apparently it could handle more without slowing the computer down.

-
+

When I was younger, I aspired to become like the "top game developers" - people who (I imagined) coded their games in pure binary. Sadly, I quickly learned that literally no one makes games in binary. In case you have thought @@ -120,7 +120,7 @@

Coin Game

@@ -145,7 +145,7 @@

Coin Game

As usual, I gave up quite quickly.

- +

"Well," you say, "what stopped you from continuing?" Good question! The whole idea of coding is fixing glitches. Trouble is, us newbies tend to unintentionally @@ -274,7 +274,7 @@

First Platformer

@@ -305,14 +305,29 @@

Conclusion

The legacy continues!

+
+
+

All comments are reviewed before being posted. No contact details + or other personal information will be shared.

+

If there's anything else you don't want published, please say + so in the comment.

+

Thanks for your feedback!

+ + +
+
+
Leave a comment - - + +
+ + +
@@ -325,8 +340,8 @@

Conclusion

© Copyright joachimford.uk - - + + - + \ No newline at end of file diff --git a/articles/create_and_animate_enemies_using_shapes.html b/articles/create_and_animate_enemies_using_shapes.html index 916aef7..9b44ec2 100644 --- a/articles/create_and_animate_enemies_using_shapes.html +++ b/articles/create_and_animate_enemies_using_shapes.html @@ -110,12 +110,12 @@

The Cubic Side Effect

Overall, this enemy has to go through four main procedures to give you the final result.

-
-

∙ Rotate in the direction it's moving (easy)

-

∙ Close and open its eyes before and after rolling (not so easy)

-

∙ Bump up and down so that it looks like its rolling along the ground (rather tricky)

-

∙ Make sure it only stops rolling when resting on an edge (tricky)

-
+
+ ∙ Rotate in the direction it's moving (easy)
+ ∙ Close and open its eyes before and after rolling (not so easy)
+ ∙ Bump up and down so that it looks like its rolling along the ground (rather tricky)
+ ∙ Make sure it only stops rolling when resting on an edge (tricky)
+

Take a look at the source code if you need to understand it better.

@@ -361,14 +361,29 @@

The ...?

if you have any more questions!

+
+
+

All comments are reviewed before being posted. No contact details + or other personal information will be shared.

+

If there's anything else you don't want published, please say + so in the comment.

+

Thanks for your feedback!

+ + +
+
+
Leave a comment - - - + + +
+ + +
@@ -381,8 +396,8 @@

The ...?

© Copyright joachimford.uk - - + + diff --git a/articles/extra/3_tips_for_game_development/18.html b/articles/extra/3_tips_for_game_development/18.html index d1f06e5..a039610 100644 --- a/articles/extra/3_tips_for_game_development/18.html +++ b/articles/extra/3_tips_for_game_development/18.html @@ -2,6 +2,7 @@ Platform Game | Joachim Ford + diff --git a/articles/extra/splash_dash_tutorial/1.png b/articles/extra/splash_dash_tutorial/1.png new file mode 100644 index 0000000..2a86058 Binary files /dev/null and b/articles/extra/splash_dash_tutorial/1.png differ diff --git a/articles/extra/splash_dash_tutorial/10.png b/articles/extra/splash_dash_tutorial/10.png new file mode 100644 index 0000000..7d03653 Binary files /dev/null and b/articles/extra/splash_dash_tutorial/10.png differ diff --git a/articles/extra/splash_dash_tutorial/11.png b/articles/extra/splash_dash_tutorial/11.png new file mode 100644 index 0000000..63e2980 Binary files /dev/null and b/articles/extra/splash_dash_tutorial/11.png differ diff --git a/articles/extra/splash_dash_tutorial/12.png b/articles/extra/splash_dash_tutorial/12.png new file mode 100644 index 0000000..1ccaeba Binary files /dev/null and b/articles/extra/splash_dash_tutorial/12.png differ diff --git a/articles/extra/splash_dash_tutorial/13.png b/articles/extra/splash_dash_tutorial/13.png new file mode 100644 index 0000000..0a7cab2 Binary files /dev/null and b/articles/extra/splash_dash_tutorial/13.png differ diff --git a/articles/extra/splash_dash_tutorial/14.png b/articles/extra/splash_dash_tutorial/14.png new file mode 100644 index 0000000..fe56871 Binary files /dev/null and b/articles/extra/splash_dash_tutorial/14.png differ diff --git a/articles/extra/splash_dash_tutorial/15.png b/articles/extra/splash_dash_tutorial/15.png new file mode 100644 index 0000000..d732188 Binary files /dev/null and b/articles/extra/splash_dash_tutorial/15.png differ diff --git a/articles/extra/splash_dash_tutorial/16.png b/articles/extra/splash_dash_tutorial/16.png new file mode 100644 index 0000000..3f4e622 Binary files /dev/null and b/articles/extra/splash_dash_tutorial/16.png differ diff --git a/articles/extra/splash_dash_tutorial/17.png b/articles/extra/splash_dash_tutorial/17.png new file mode 100644 index 0000000..31726bb Binary files /dev/null and b/articles/extra/splash_dash_tutorial/17.png differ diff --git a/articles/extra/splash_dash_tutorial/18.png b/articles/extra/splash_dash_tutorial/18.png new file mode 100644 index 0000000..9b96840 Binary files /dev/null and b/articles/extra/splash_dash_tutorial/18.png differ diff --git a/articles/extra/splash_dash_tutorial/19.png b/articles/extra/splash_dash_tutorial/19.png new file mode 100644 index 0000000..c579842 Binary files /dev/null and b/articles/extra/splash_dash_tutorial/19.png differ diff --git a/articles/extra/splash_dash_tutorial/2.html b/articles/extra/splash_dash_tutorial/2.html new file mode 100644 index 0000000..a6d327f --- /dev/null +++ b/articles/extra/splash_dash_tutorial/2.html @@ -0,0 +1,98 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/articles/extra/splash_dash_tutorial/3.png b/articles/extra/splash_dash_tutorial/3.png new file mode 100644 index 0000000..ad4668a Binary files /dev/null and b/articles/extra/splash_dash_tutorial/3.png differ diff --git a/articles/extra/splash_dash_tutorial/4.png b/articles/extra/splash_dash_tutorial/4.png new file mode 100644 index 0000000..7bfe35e Binary files /dev/null and b/articles/extra/splash_dash_tutorial/4.png differ diff --git a/articles/extra/splash_dash_tutorial/5.png b/articles/extra/splash_dash_tutorial/5.png new file mode 100644 index 0000000..bb6ea89 Binary files /dev/null and b/articles/extra/splash_dash_tutorial/5.png differ diff --git a/articles/extra/splash_dash_tutorial/6.png b/articles/extra/splash_dash_tutorial/6.png new file mode 100644 index 0000000..0cceee4 Binary files /dev/null and b/articles/extra/splash_dash_tutorial/6.png differ diff --git a/articles/extra/splash_dash_tutorial/7.png b/articles/extra/splash_dash_tutorial/7.png new file mode 100644 index 0000000..890ea1e Binary files /dev/null and b/articles/extra/splash_dash_tutorial/7.png differ diff --git a/articles/extra/splash_dash_tutorial/8.png b/articles/extra/splash_dash_tutorial/8.png new file mode 100644 index 0000000..1078f3d Binary files /dev/null and b/articles/extra/splash_dash_tutorial/8.png differ diff --git a/articles/extra/splash_dash_tutorial/9.png b/articles/extra/splash_dash_tutorial/9.png new file mode 100644 index 0000000..636bcd7 Binary files /dev/null and b/articles/extra/splash_dash_tutorial/9.png differ diff --git a/articles/extra/splash_dash_tutorial/basicWater.html b/articles/extra/splash_dash_tutorial/basicWater.html new file mode 100644 index 0000000..5f0add2 --- /dev/null +++ b/articles/extra/splash_dash_tutorial/basicWater.html @@ -0,0 +1,71 @@ +Basic Water | Joachim Ford + + + \ No newline at end of file diff --git a/articles/extra/splash_dash_tutorial/blocks.html b/articles/extra/splash_dash_tutorial/blocks.html new file mode 100644 index 0000000..42726cf --- /dev/null +++ b/articles/extra/splash_dash_tutorial/blocks.html @@ -0,0 +1,36 @@ +Blocks | Joachim Ford + + + \ No newline at end of file diff --git a/articles/extra/splash_dash_tutorial/character.html b/articles/extra/splash_dash_tutorial/character.html new file mode 100644 index 0000000..3506894 --- /dev/null +++ b/articles/extra/splash_dash_tutorial/character.html @@ -0,0 +1,28 @@ +Character | Joachim Ford + + + \ No newline at end of file diff --git a/articles/extra/splash_dash_tutorial/coins.html b/articles/extra/splash_dash_tutorial/coins.html new file mode 100644 index 0000000..5022228 --- /dev/null +++ b/articles/extra/splash_dash_tutorial/coins.html @@ -0,0 +1,62 @@ +Coins | Joachim Ford + + + \ No newline at end of file diff --git a/articles/extra/splash_dash_tutorial/collectCoins.html b/articles/extra/splash_dash_tutorial/collectCoins.html new file mode 100644 index 0000000..6ff02e3 --- /dev/null +++ b/articles/extra/splash_dash_tutorial/collectCoins.html @@ -0,0 +1,68 @@ +Collect Coins | Joachim Ford + + + \ No newline at end of file diff --git a/articles/extra/splash_dash_tutorial/collision.html b/articles/extra/splash_dash_tutorial/collision.html new file mode 100644 index 0000000..c9155f3 --- /dev/null +++ b/articles/extra/splash_dash_tutorial/collision.html @@ -0,0 +1,61 @@ +Collision | Joachim Ford + + + \ No newline at end of file diff --git a/articles/extra/splash_dash_tutorial/color.html b/articles/extra/splash_dash_tutorial/color.html new file mode 100644 index 0000000..687dcac --- /dev/null +++ b/articles/extra/splash_dash_tutorial/color.html @@ -0,0 +1,62 @@ +Color | Joachim Ford + + + \ No newline at end of file diff --git a/articles/extra/splash_dash_tutorial/control.html b/articles/extra/splash_dash_tutorial/control.html new file mode 100644 index 0000000..d1cb917 --- /dev/null +++ b/articles/extra/splash_dash_tutorial/control.html @@ -0,0 +1,44 @@ +Control | Joachim Ford + + + \ No newline at end of file diff --git a/articles/extra/splash_dash_tutorial/finalDetails.html b/articles/extra/splash_dash_tutorial/finalDetails.html new file mode 100644 index 0000000..de75c40 --- /dev/null +++ b/articles/extra/splash_dash_tutorial/finalDetails.html @@ -0,0 +1,98 @@ +Details | Joachim Ford + + + \ No newline at end of file diff --git a/articles/extra/splash_dash_tutorial/floatingCoins.html b/articles/extra/splash_dash_tutorial/floatingCoins.html new file mode 100644 index 0000000..e1394c5 --- /dev/null +++ b/articles/extra/splash_dash_tutorial/floatingCoins.html @@ -0,0 +1,68 @@ +Floating Coins | Joachim Ford + + + \ No newline at end of file diff --git a/articles/extra/splash_dash_tutorial/gravity.html b/articles/extra/splash_dash_tutorial/gravity.html new file mode 100644 index 0000000..4b2f945 --- /dev/null +++ b/articles/extra/splash_dash_tutorial/gravity.html @@ -0,0 +1,47 @@ +Gravity | Joachim Ford + + + \ No newline at end of file diff --git a/articles/extra/splash_dash_tutorial/jump.html b/articles/extra/splash_dash_tutorial/jump.html new file mode 100644 index 0000000..1a0ff95 --- /dev/null +++ b/articles/extra/splash_dash_tutorial/jump.html @@ -0,0 +1,62 @@ +Jump | Joachim Ford + + + \ No newline at end of file diff --git a/articles/extra/splash_dash_tutorial/newLevel.html b/articles/extra/splash_dash_tutorial/newLevel.html new file mode 100644 index 0000000..fdba7cd --- /dev/null +++ b/articles/extra/splash_dash_tutorial/newLevel.html @@ -0,0 +1,86 @@ +New Level | Joachim Ford + + + \ No newline at end of file diff --git a/articles/extra/splash_dash_tutorial/newLevelImproved.html b/articles/extra/splash_dash_tutorial/newLevelImproved.html new file mode 100644 index 0000000..83cbe1c --- /dev/null +++ b/articles/extra/splash_dash_tutorial/newLevelImproved.html @@ -0,0 +1,86 @@ +New Level | Joachim Ford + + + \ No newline at end of file diff --git a/articles/extra/splash_dash_tutorial/springJump.html b/articles/extra/splash_dash_tutorial/springJump.html new file mode 100644 index 0000000..c057569 --- /dev/null +++ b/articles/extra/splash_dash_tutorial/springJump.html @@ -0,0 +1,62 @@ +Spring Jump | Joachim Ford + + + \ No newline at end of file diff --git a/articles/extra/splash_dash_tutorial/surface.html b/articles/extra/splash_dash_tutorial/surface.html new file mode 100644 index 0000000..32a3022 --- /dev/null +++ b/articles/extra/splash_dash_tutorial/surface.html @@ -0,0 +1,62 @@ +Surface | Joachim Ford + + + \ No newline at end of file diff --git a/articles/extra/splash_dash_tutorial/water.html b/articles/extra/splash_dash_tutorial/water.html new file mode 100644 index 0000000..03d2940 --- /dev/null +++ b/articles/extra/splash_dash_tutorial/water.html @@ -0,0 +1,80 @@ +Water | Joachim Ford + + + \ No newline at end of file diff --git a/articles/extra/splash_dash_tutorial/zoom/1.png b/articles/extra/splash_dash_tutorial/zoom/1.png new file mode 100644 index 0000000..1df2198 Binary files /dev/null and b/articles/extra/splash_dash_tutorial/zoom/1.png differ diff --git a/articles/extra/splash_dash_tutorial/zoom/2.png b/articles/extra/splash_dash_tutorial/zoom/2.png new file mode 100644 index 0000000..a19cf42 Binary files /dev/null and b/articles/extra/splash_dash_tutorial/zoom/2.png differ diff --git a/articles/extra/splash_dash_tutorial/zoom/3.png b/articles/extra/splash_dash_tutorial/zoom/3.png new file mode 100644 index 0000000..53be88f Binary files /dev/null and b/articles/extra/splash_dash_tutorial/zoom/3.png differ diff --git a/articles/extra/splash_dash_tutorial/zoom/4.png b/articles/extra/splash_dash_tutorial/zoom/4.png new file mode 100644 index 0000000..f25f113 Binary files /dev/null and b/articles/extra/splash_dash_tutorial/zoom/4.png differ diff --git a/articles/extra/using_inverse_kinematics_to_animate_your_character/3.html b/articles/extra/using_inverse_kinematics_to_animate_your_character/3.html index 6f4d412..4e99e9f 100644 --- a/articles/extra/using_inverse_kinematics_to_animate_your_character/3.html +++ b/articles/extra/using_inverse_kinematics_to_animate_your_character/3.html @@ -2,6 +2,11 @@ + + + +
+
+ + +
+ +
+ 04.08.2024 +

The Splash Dash Tutorial

+ + +
+

The game Splash Dash was my entry to a competition running from July to August 2024. + In this article, I'm going to try to explain how it was made, line by line.

+

When I started writing this, I sort of forgot how difficult these + projects are to explain. We will go into very extreme detail - but no pressure! + Feel free to flick through it at your own pace.

+ + + +
<canvas id=c><script>for(p='(e,d)Q<17&!PeightO0,NNNWLr(KfoKJ=d:Ikey x,y=c./2o.fillRect(+.),4,.1e==>S(e=innerd=(i-1Styl"#+=-e)*u+HA(d5,e(d,eA)|	5)M[i-d)*u+W,(&&(=Q=>*Math.sin(+1]==e. Codel=n=>{JWwidthWidth6,HhOHO6,ogetContext`2d`,u=(W+H)5,r=,l,f)=>l,u*f,u*fC=d,F=e,g&s?f=-.18:ef.007,h?d-=.1:qd.1t,KLKs=N9,WA&!	&!)e%1<fF,s=1,f=0	)|A||d<0|d5>70C,	)f=NF)ePA)&a)>k[~~d*~~e]=1R=Nx=70;x--;)Jy=30;y--;)"+~(5x)-543A()?K,1):a()>&yPk[x*y]ff0",Kx4,y41p/50+x+R++.3));JK-W,NWK7L034",L-m*W/5e3,H/9r+f,.4rKd3,e0698",i=634;i-=2;o.lineTo(/9]+13))p?(]*>3(]02p*)-),~=~/9)&e>12&e<14?f/4:*=.97*<630-=]5):M.push(N0);KN13,70(R?++m>5e3?C:p9:T>4?t="#ff0":k=[T*=14,Tm=1],requestAnimationFrame(l)},f=g=h=q=s=u=T=m=p=Nk=[M=[]],t="#3897",S38|87?gI39|68?qI37|65?hINon down,1on upa(1+~~e,~~d,6d*e*9+T)+d5AaQ>Nl()';G=/[^!-HMR-~]/.exec(p);)with(p.split(G))p=join(shift());eval(p)</script>
+
+ + + +
+

Introduction

+

Code golfing - you might have heard the term before. If you haven't, + let me clarify that we're talking about a different kind of golf. + "Code Golfing" is the art of condensing a code program into the smallest + possible space. Code programs have the potential to be massive, but + when you golf them you are - in effect - guiding that golf ball into the hole. + You're aiming for the perfect, most concise combination of letters to create + a functional but extremely compact code program.

+

The smallest unit of memory is a bit. + A single letter is made of 8 bits, which is called a byte. + One day, a programmer said to another programmer: "The goal is, + one thousand bytes to make a game." And that's code golfing.

+

To explain how small a 1k game really is, I'm going to use the example + of the standard iPhone. At the time of writing, it has the capacity to store about + 256 gigabytes of data. If every game in the world only took up one kilobyte + of memory, you would potentially be able to store one million of them on your + iPhone alone. Here's a zoom-out of how many games that would be.

+
+ + + +
+

I can't imagine there are many people who would actually want + a million games on their iPhone, but that's beside the point.

+ +

In the past, programmers had to be good at code golfing because their + computers didn't have as much memory as they do now. + Pretty much the only reason why it still exists nowadays is because it makes + people "feel good". It's just something about having a few of + lines of code, and knowing it's the basis for a complete, + playable game - where every letter and every symbol is part of the + reason why the game exists.

+
+ +
+

Coding the Game From Scratch

+

Before and during the competition, I set myself the + challenge of making a decent platformer in the smallest possible + amount of bytes. The rest of this article will explain the entire + development of Splash Dash.

+ +

Step 1. Setting Up

+

We're going to start with an empty HTML file. That's the + language every website thinks in, so even if we're want to + write in JavaScript, we have to initialise it with HTML. + +

To start off, let's create a canvas and open JavaScript. + An HTML canvas, like the name suggests, allows us to + figuratively "draw" things on the screen. In our + case, we're going to draw our game onto the canvas.

+ +
<canvas id=c></canvas> // Make a canvas
+<script></script> // Initialise JavaScript
+ +

The <script> tags allow us to start + writing in JavaScript. Let's create a function that runs every single frame. + This will allow us to progressively render the game onto the + canvas, instead of simply drawing a static image.

+ +
+ +

To visualise this, consider how a flipbook works. It's + made of a series of "frames," and by flipping the pages we can + get a decent animation. Similarly, instead of drawing a single + "frame" onto our canvas, we need to keep updating it to allow for + an interactive game. +

+
+ +
+#<script>
+
+l=_=>{ // Initialise the update loop
+requestAnimationFrame(l) // Update the loop every frame
+}
+l() // Start running the loop when the game starts
+
+#</script>
+
+ +

In the code above, we create a function called l + (short for loop), we tell the computer to run this function every frame by + using requestAnimationFrame(), and finally we begin + running our funtion as soon as the game starts.

+ +

Inside the update loop, let's make the screen responsive by setting the + dimensions of the canvas to the dimensions of the browser window. We will + also create the context. The context is the figurative "paintbrush" + that allows us to draw things on the canvas.

+ +
+#l=_=>{
+
+// Get the dimensions of the canvas, while at the same time setting its dimensions
+W=c.width=innerWidth-16
+H=c.height=innerHeight-16
+
+#requestAnimationFrame(l)
+#}
+
+// Initialise the 2d context. Backticks mean we don't need to use brackets
+X=c.getContext`2d`
+
+#l()
+
+ +

That's the set up done - we've created the canvas, the paintbrush, + and a function that runs every 60th of a second. But we still just have a blank screen! + Let's add the player!

+ +

Step 2. The Character

+

In most cases, getting the main character sorted early on + is a good idea. Because I was trying to use as few bytes as + possible, I created a very simple box figure with eyes and feet. + The former had to be sacrificed because of addition of the water + simulation.

+ +
+Run Code +
+#l=_=>{
+#W=c.width=innerWidth-16
+#H=c.height=innerHeight-16
+
+// Get the average size of the webpage by adding the width and height together
+z=(W+H)/25
+
+// Make a function the draws rectangles
+r=(x,y,s)=>X.fillRect((x-d)*z+W/2,(y-e)*z+H/2,z*s,z*s)
+
+X.fillStyle='#034' // Body colour
+r(d,e,.4) // Body
+r(d,e+.4,.1) // Left foot
+r(d+.3,e+.4,.1) // Right foot
+
+#requestAnimationFrame(l)
+#}
+#X=c.getContext`2d`
+
+// Define the X and Y coordinates of the player
+d=e=0
+
+#l()
+

There are quite a lot of additions in the code above, so let's break it down. + Before we run the update loop, we define the + x and y positions + of the player, stored as d and + e.

+ +

In the update loop, we create a simple function can draw squares + on the screen. Since every shape in the game is a square and is drawn + based on the player position - which also happens to be the camera's - + the function only needs three arguments: + x, y, and size. All drawing and offsetting is calculated in the function.

+ +

After we create the function r(x, y, s), we + can draw the player with hardly any code. We set the fill style + of the player to determine its colour, and then we simply draw the body + and feet based on d and e. + I've repeated the relevant code below. + Take a look at the commented areas if you don't understand.

+ +
+Run Code +
// Make a function the draws rectangles
+r=(x,y,s)=>X.fillRect((x-d)*z+W/2,(y-e)*z+H/2,z*s,z*s)
+
+X.fillStyle='#034' // Body colour
+r(d,e,.4) // Body
+r(d,e+.4,.1) // Left foot
+r(d+.3,e+.4,.1) // Right foot
+
+ +
+ +
+

Step 3. Blocks and Gravity

+

Now we have a basic character, we can start designing the world + it inhabits. Let's write a small patch of code to create some ground.

+ +
+Run Code +
#r=(x,y,s)=>X.fillRect((x-d)*z+W/2,(y-e)*z+H/2,z*s,z*s)
+
+// Set the fill colour to orange
+X.fillStyle='#f80'
+for(x=70;x--;){ // Start at 70 and count down
+    for(y=30;y--;){ // Start at 30 and count down
+        if(y)r(x,y,.9) // Draw blocks
+    }
+}
+
+#X.fillStyle='#034' // Body colour
+#r(d,e,.4) // Body
+ +

This is what it should look like so far.

+
+ +
+ +
+

Things are taking shape! At the moment, the main character is + static, and can't move. The following code snippet includes + checks for arrow or WASD keyboard inputs, which allows for player movement.

+ +
+Run Code +
+#l=_=>{
+#W=c.width=innerWidth-16
+#H=c.height=innerHeight-16
+
+// Move player
+h?d-=.1:j?d+=.1:0
+
+#(...)
+
+#}
+#X=c.getContext`2d`
+
+// Define new variables
+d=e=g=h=j=0
+
+// Detect key presses
+P=(e,b)=>e.keyCode==38|e.keyCode==87?g=b:e.keyCode==39|e.keyCode==68?j=b:e.keyCode==37|e.keyCode==65?h=b:0
+onkeydown=e=>P(e,1)
+onkeyup=e=>P(e)
+
+#l()
+
+ +

The above code adds three new variables: g, + the up key, which we will use shortly, h, + the left key, and j, the right key. + These numbers are 1 when their corresponding + key is pressed, but zero any other time.

+

Back in the update loop, we check whether the left or right key + is pressed, and then move the player's x position accordingly. + A very compact alternative to writing if + and else, is by using the + ? and : operators. + In effect, h?d-=.1:j?d+=.1:0 does exactly + the same thing as this:

+
if (h == true) {
+    d -= .1
+}
+
+else if (j == true) {
+    d += .1
+}
+ +

If you run the code so far, you'll realise that the player can + move left and right, but doesn't fall onto the ground. This calls + for the addition of gravity. Here's the implementation of that.

+ +
+Run Code +
+#l=_=>{
+#W=c.width=innerWidth-16
+#H=c.height=innerHeight-16
+
+// Moved player based on gravity
+e+=f+=.007
+#h?d-=.1:j?d+=.1:0
+
+#(...)
+
+#}
+#X=c.getContext`2d`
+
+// Add a new number "f"
+d=e=g=h=j=f=0
+
+#P=(e,b)=>e.keyCode==38|e.keyCode==87?g=b:e.keyCode==39|e.keyCode==68?j=b:e.keyCode==37|e.keyCode==65?h=b:0
+#onkeydown=e=>P(e,1)
+#onkeyup=e=>P(e)
+
+#l()
+
+ +

The new variable f refers to the y + velocity or "fall speed" of the player. Every frame, we make + this y velocity increase by a small amount, and make the y position + of the player increase based in this velocity. This gives us a + simple but effective illusion of gravity.

+
+ +
+

Step 4. Tiny Collision

+ +

One of the most difficult parts of a platform + game is what is known as "Collision Response". Collision response + is the maths that stops the main character from falling through the + ground. So it's pretty important. If you run the code above, you + will notice that the player has no interaction with the orange blocks.

+

The more edges you add to a collision response calculation, the harder + the maths becomes. For example, let's say we have an infinite stretch + of perfectly flat ground. Let's visualise that.

+
+ +
+ +
+

For the falling ball to have collision with the ground, we would simply + use the following calculation: If the ball is too low, place it on the ground.

+ +
 if (ballY > groundY)
+    ballY = groundY
+ +

But if we alter the line slightly, we are already faced with + two problems, both of which have been circled in the image below.

+

With our current calculation, + if the ball rolls onto the side of the jut, it will instantly + jump to the top. And if it hits a corner, we would expect it to + bounce off at a diagonal. So already we're having to go into pretty + advaced maths to figure out a system that not only detects + what part of the line the ball is intersecting with, but also + determining a valid amount by which the ball should be moved away.

+
+ +
+ +
+

In the end, I decided to go with the simplest approach, although + admittedly not a very accurate one. Here's a summary of what the code + needs to do.

+ +
+ • Remember the player's position from the last frame
+ • If the player is touching a block:
+   • Move the player to its previous position
+
+ +

The issue with this approach is the fact that the faster the player + is moving the less accurate the results will be. Where the player should + have simply been pushed to the side of the block, his position is reset all + the way back to where he was a frame ago. But we're dealing with 1 kilobyte + here, so it doesn't matter.

+

The code below makes the player have good collision response with + each block. Notice how we store the previous position of the player by getting + his position before we move it.

+ +
+Run Code +
#W=c.width=innerWidth-16
+#H=c.height=innerHeight-16
+
+// Get current player pos
+D=d
+E=e
+
+#e+=f+=.007
+#h?d-=.1:j?d+=.1:0
+
+#z=(W+H)/25
+#r=(x,y,s)=>X.fillRect((x-d)*z+W/2,(y-e)*z+H/2,z*s,z*s)
+
+// Detect and resolve collision
+A(d,e+.5)&!A(d,e)|A(d+.5,e+.5)&!A(d+.5,e)&&(e+.5)%1<f&&(e=E,f=0) // Ground
+A(d,e)|A(d+.5,e)|A(d,e+.5)|A(d+.5,e+.5)|d<0|d+.5>70&&(d=D,A(d,e)|A(d+.5,e)&&(f=0,e=E)) // Wall and Ceiling
+
+#X.fillStyle='#f80'
+#for(x=70;x--;){
+#    for(y=30;y--;){
+
+#(...)
+
+#onkeydown=e=>P(e,1)
+#onkeyup=e=>P(e)
+
+// Create two new functions that return based on given block coordinates
+a=(x,y)=>{x=~~x,y=~~y;return y}
+A=(x,y)=>a(x,y)>0
+#l()
+
+
+

If you run the code above you'll realise that the player now collides + with the ground. So what's changed? At the top of the update loop, we get the + previous player position. Further down, we detect collision by + checking the cells around the player. Before we get into how the collision + actually works, + I'm going to take a minute to explain what our two new functions do, + a(x, y) and A(x, y).

+ +

The new a(x, y) function we created + rounds given coordinates down to a whole number - + that's what the ~~ means - and returns + a value for the current position. What does that mean?

+

I'm going to ask you to think abstract for a moment. Consider this: + each block in our game has a size of one. Let's say our + player is at x coordinate 3.5 and y coordinate + 2.1, and a block has its top-left corner + at x coordinate 3 and y coordinate + 2.

+
+ +
+ +
+

To check if the player is colliding with the block, we simply + round the player's position down to the nearest whole number. + After that, all you need to do is check if the player's rounded + position matches the block's position. This is a very simple + way to check collision with blocks that are ordered in a grid. + In fact, a similar technique was used in the game + Io's Mission.

+ +

Anyway, back to the code. Let's review the two functions again.

+ +
+// Create two new functions that return based on given block coordinates
+a=(x,y)=>{x=~~x,y=~~y;return y}
+A=(x,y)=>a(x,y)>0
+
+ +

The a(x, y) + function is given a set of coordinates, + rounds the coordinates down to determine which block we're dealing with, and then + returns a number. This "returned number" represents whether the original coordinates + are colliding with a block or not. If they are, the function will return a number + above zero. If they're not, it will return a number that is zero or under.

+ +

Since our world is just a stretch of flat ground, all we need to do is return + the y position of the block. That's what the function is doing where it says + return y.

+

The first row of blocks - where the player + starts - has a y coordinate of 0, and we know + there aren't any blocks there. + But for the remaining rows, since their y position will always be greater than zero, + the computer will recognise them as blocks that need to be collided with.

+ +

The A(x, y) function detects if the coordinate + should be a block or not by simply checking if our a(x, y) + function returns a number above than zero.

+ +

That was the complicated part! Now we can get back to the code in the update + loop, where we actually do the calculations for block collision.

+ +
+// Get current player pos
+D=d
+E=e
+
+// Move the player
+e+=f+=.007
+h?d-=.1:j?d+=.1:0
+
+#(...)
+
+// Detect and resolve collision
+A(d,e+.5)&!A(d,e)|A(d+.5,e+.5)&!A(d+.5,e)&&(e+.5)%1<f&&(e=E,f=0) // Ground
+A(d,e)|A(d+.5,e)|A(d,e+.5)|A(d+.5,e+.5)|d<0|d+.5>70&&(d=D,A(d,e)|A(d+.5,e)&&(f=0,e=E)) // Wall and Ceiling
+
+ +

Let's take the first line, which detects if the player is standing + on the ground. + I'm going to translate it progressively, but if you don't understand, + don't worry! The break-down below is basically explaining how the + code detects when the player is standing on the ground.

+ +
+

A(d,e+.5) If the bottom left cell + just under the player is a solid block...

+ +
+ +

Remember the player is only half the size of a block, + so we use .5 to access the block + below him.

+
+ +

&!A(d,e) and the top left + of the player is not ! touching anything...

+ +
+ +

Remember JavaScript works from the top left, so + d and e refer + to the top left of the player.

+
+ +

|A(d+.5,e+.5) or the bottom right cell + just below the player is a solid block...
+ + &!A(d+.5,e) and the top right of the + player is not touching anything...

+ +

&&(e+.5)%1<f Then - that's what + the && means - if the player's current + y position plus his fall speed is enough to actually touch the + blocks underneath him...

+ +

&&(e=E,f=0) Then we finally know + for sure that the player is on the ground. We revert his y position to + what it was on the frame before, and we set his y velocity + (or "fall speed") to zero, because - + naturally - he's not falling anymore.

+
+ +

If you want to understand the second line, I'm going to leave + it for you to break down by yourself. If you're really stuck, feel + free to ask in the comment section at the end of this page.

+
+ +
+

Step 5. Jumping

+

Well, this is quite an easy section - a little intermission for you!

+

Understandably, the player shouldn't be able to jump from mid-air, + but has to be standing on something beforehand. Since we now know when + the player is on the ground, we can add this ability. Let's update our code!

+ +
+Run Code +
+<canvas id=c></canvas>
+<script>
+#l=T=>{
+#W=c.width=innerWidth-16
+#H=c.height=innerHeight-16
+
+#D=d
+#E=e
+
+// Implement jump
+g&v?f=-.18:e+=f+=.007
+h?d-=.1:j?d+=.1:0
+
+#z=(W+H)/25
+#r=(x,y,s)=>X.fillRect((x-d)*z+W/2,(y-e)*z+H/2,z*s,z*s)
+
+// Before collision, say the player is in the air
+v=0
+
+// Update collision
+A(d,e+.5)&!A(d,e)|A(d+.5,e+.5)&!A(d+.5,e)&&(e+.5)%1<f&&(e=E,v=1,f=0)
+#A(d,e)|A(d+.5,e)|A(d,e+.5)|A(d+.5,e+.5)|d<0|d+.5>70&&(d=D,A(d,e)|A(d+.5,e)&&(f=0,e=E))
+
+#X.fillStyle='#f80'
+#for(x=70;x--;){
+#    for(y=30;y--;){
+#        if(y)r(x,y,.9) // Draw blocks
+#    }
+#}
+
+#X.fillStyle='#034' // Body colour
+#r(d,e,.4) // Body
+#r(d,e+.4,.1) // Left foot
+#r(d+.3,e+.4,.1) // Right foot
+
+#requestAnimationFrame(l)
+#}
+#X=c.getContext`2d`
+
+// Add a new number "v"
+d=e=g=h=j=f=v=0
+
+#P=(e,b)=>e.keyCode==38|e.keyCode==87?g=b:e.keyCode==39|e.keyCode==68?j=b:e.keyCode==37|e.keyCode==65?h=b:0
+#onkeydown=e=>P(e,1)
+#onkeyup=e=>P(e)
+
+#a=(x,y)=>{x=~~x,y=~~y;return y}
+#A=(x,y)=>a(x,y)>0
+#l()
+</script>
+
+ +

The above code adds a new variable v, + which is a number representing whether the player is on the ground or not. If + the player is on the ground and the up key is pressed, we can change his y + velocity.

+

Notice that on the line where we detect ground collision, we've added + the code v=1. Consequently, + v is 1 when the + player is on the ground, and zero any other time.

+

While we're at it, let's make the jump slightly more interesting + by springing the character's body up a little. This is not complicated, + since we already know the player's y velocity, f.

+ +
+Run Code +
+#X.fillStyle='#034' // Body colour
+r(d,e+f,.4) // Body
+#r(d,e+.4,.1) // Left foot
+#r(d+.3,e+.4,.1) // Right foot
+
+
+ +
+

Step 6. Making Worlds

+

Out of everything in Splash Dash's the development, + I think making the levels was the most enjoyable + part. It was really amaizing to + see how it all came together at the end.

+ +

Part 1. The Basic Formula

+

Naturally, being a project designed to be as small as possible, + manually designing each world wasn't an option. I had to think of + a formula that would generate worlds that not only looked good, + but would also be fun and - more importantly - possible to play.

+

The first stage was to make the formula. Before we start, we need to make a + small improvement to the code that draws blocks on the screen. Instead of writing + if(y)r(x,y,.9), we can now use our fancy + A(x, y) function to make it mokre consistent.

+

We've also learned that we can use the shorthand + ? and : + operators instead of if.

+ +
+X.fillStyle='#f80'
+for(x=70;x--;){
+    for(y=30;y--;){
+        A(x,y)?r(x,y,.9):0 // Draw blocks
+    }
+}
+
+ +

I fiddled around for quite some time getting the formula right, + but it still felt like a fluke when I finally got a good one - sometimes + trial and error is the only option! After a while, I stumbled upon the code + shown below. I've edited the a(x, y) function + because that's the thing that says whether a given coordinate + should be a block or not.

+

If we want to generate worlds, we just + distort these coordinates so that the returned number + gives us a nice looking surface.

+ +
+Run Code +
a=(x,y)=>{x=~~x+1,y=~~y;return (Math.sin(y*x*9)*6)+y-15}
+ +

Further up un our update loop, we can also give these blocks + more natural colours.

+ +
+Run Code +
+for(x=70;x--;){
+    for(y=30;y--;){
+        X.fillStyle='#'+~(Math.sin(x)*5-543) // Random number representing hexadecimal values
+        A(x,y)?r(x,y,.9):0
+    }
+}
+
+ +
+ +

It's not looking too bad! I thought the generation formula was + completely sorted at this point, but I later realised it needed some tweaking. + Let's not worry about that for now though, and push ahead with adding some coins!

+ +

Part 2. Adding Coins

+ +

Let's first of all get these coins on the screen, and then we + can think about how the player collects them.

+

We can use our function a(x, y) + to decide whether a coin should be placed on a block or not.

+ +
+Run Code +
+#for(x=70;x--;){
+#    for(y=30;y--;){
+#        X.fillStyle='#'+~(Math.sin(x)*5-543)
+        A(x,y)?r(x,y,.9):a(x,y)>-1&y<17&&(X.fillStyle='#bb0',r(x+.4,y+.4,.3))
+#    }
+#}
+
+ +

Well, they look a bit like coins, so that should be enough for now.

+

In the code above, we added a check to decide whether a coin should be + placed or not. Do you remember earlier, we checked if a block should be placed + if our function a(x, y) returned a number that + was greater than zero? In a similar way, the code above checks if a coin + should be placed if the returned number is greater than minus one. + We make sure coins are never placed inside blocks by putting it after the + : operator, which in this case means + else. In effect, the code above is doing this:

+ +
+if (number > 0)
+    // Draw block
+
+else if (number > -1)
+    // Draw coin
+
+ +

Finally, to prevent coins from being created to low down, + we added &y<17, which ensures that coins are never + created after the 16th row.

+ +
+

Unlike a lot of features in this game, coins have to be stored. + That means, where all the blocks in the world are solely calculated + based on their position, the thing that decides if a coins + exists is based on whether the player has collected it or not.

+

Because of this, we need to create our first + array - or "list" - that allows us to keep track of all the coins that + the player has touched.

+ +
+#d=e=g=h=j=f=v=0
+
+// Create an array to store collected coins
+k=[]
+
+#P=(e,b)=>e.keyCode==38|e.keyCode==87?g=b:e.keyCode==39|e.keyCode==68?j=b:e.keyCode==37|e.keyCode==65?h=b:0
+#onkeydown=e=>P(e,1)
+#onkeyup=e=>P(e)
+
+ +

Just after we resolve block collision, we can check if the player is touching a coin. + This check uses the same method we used to draw the coins on the screen. Further + down the code, we can add the line &!k[x*y] to make sure + collected coins aren't drawn.

+ +
+Run Code +
+#A(d,e+.5)&!A(d,e)|A(d+.5,e+.5)&!A(d+.5,e)&&(e+.5)%1<f&&(e=E,v=1,f=0) // Ground
+#A(d,e)|A(d+.5,e)|A(d,e+.5)|A(d+.5,e+.5)|d<0|d+.5>70&&(d=D,A(d,e)|A(d+.5,e)&&(f=0,e=E)) // Wall and Ceiling
+
+// Coin collision
+e<17&a(d,e)>-1&&(k[~~d*~~e]=1)
+
+#for(x=70;x--;){
+#    for(y=30;y--;){
+#        X.fillStyle='#'+~(Math.sin(x)*5-543)
+
+        // Only draw coin if it hasn't been collected
+        A(x,y)?r(x,y,.9):a(x,y)>-1&y<17&!k[x*y]&&(X.fillStyle='#bb0',r(x+.4,y+.4,.3))
+#    }
+#}
+
+ +

While we're at it, let's make the coins float by animating their + y position...

+ +
+Run Code +
+#for(x=70;x--;){
+#    for(y=30;y--;){
+#        X.fillStyle='#'+~(Math.sin(x)*5-543)
+        A(x,y)?r(x,y,.9):a(x,y)>-1&y<17&!k[x*y]&&(X.fillStyle='#bb0',r(x+.4,y+.4+Math.sin(_/50+x)*.1,.3))
+#    }
+#}
+
+
+ +
+

Part 3. Simulating Water

+

Now for the water simulation! The technique used in the game + is probably the simplest form of water possible. It involves + having a row of "points," which can move up and down based on their + neighbour's y position.

+

The fundamental structure of this kind of water simulation is + that all points are trying to reach equilibrium. If an adjacent + point is higher or lower than the first one, the first point will + speed up or slow down accordingly to balance with its neighbour.

+
+ +
+ +
+

Before we implement that, let's see what it looks like to add + some flat water to the world.

+ +
+Run Code +
+#r(d,e+.4,.1)
+#r(d+.3,e+.4,.1)
+
+// Make a blue rectangle
+X.fillStyle='#0698'
+r(0,13,70)
+
+#requestAnimationFrame(l)
+
+ +
+ +

The bulk of the water looks okay, but the surface needs to be + more fluid. In the code above, we simply created a transparent blue + rectangle and rendered it on top of everything. Let's implment + the method we described in the outset of this section.

+ +
+Run Code +
+#r(d,e+.4,.1)
+#r(d+.3,e+.4,.1)
+
+X.fillStyle='#0698'
+
+// Run through all 317 water points and draw them on the screen
+for(i=634;i-=2;X.lineTo((i/9-d)*z+W/2,(J[i]+13-e)*z+H/2)){
+// A the start of the program, fill the array with numbers
+if(!_)J.push(0,0)
+
+// Calculate the water simulation and detect player collision
+else{
+J[i]+=J[i+1]*(i>3)
+J[i+1]+=(J[i-1]+Math.sin(_*J[i+1])*.02-J[i+1])/2
+~d==~(i/9)&e>12&e<14?J[i+1]+=f/4:J[i+1]*=.97*(i<630)
+J[i+1]-=J[i]/25
+}}
+
+r(0,13,70)
+
+// Draw the water surface on the screen
+X.fill()
+
+#requestAnimationFrame(l)
+#}
+#X=c.getContext`2d`
+
+#d=e=g=h=j=f=v=0
+
+// Create an array called J to store the water
+k=[J=[]]
+
+
+ +

It works, but what's the code doing? First, we create a new + array - J - to store all the points + for the surface of the water. In the update loop, we run a process + that starts at 634 and counts down by two after every run. What does that mean?

+

The method used in the code is known as a + "for-loop". A for-loop can allow you to run a process + multiple times, with a number representing which "run" it's on. + In our case, we started our number at 634 and counted down in twos + until we reached zero.

+ +
+for(i=634;i-=2;)
+ +

Then we checked how long the game had been running for. The code + said that if the game was on the first frame, fill our water array + with points. Otherwise, calculate and render the water on the screen.

+ +
if(!_)J.push(0,0)
+else{
+J[i]+=J[i+1]*(i>3)
+J[i+1]+=(J[i-1]+Math.sin(_*J[i+1])*.02-J[i+1])/2
+~d==~(i/9)&e>12&e<14?J[i+1]+=f/4:J[i+1]*=.97*(i<630)
+J[i+1]-=J[i]/25
+}
+
+ +

Let's break that down.

+ +
+

J[i]+=J[i+1]*(i>3)

+

Basically, our array stores two values for each water + point: the y position and the speed. + J[i] is accessing the relevant + y position by getting array's contents at the current "index" + of the for-loop. +=J[i+1] makes the + y position increase or decrease based on its speed. + The last part, *(i>3), is trivial, but + solves a small glitch at the side of the world.

+
+ +
+

J[i+1]+=(J[i-1]+Math.sin(_*J[i+1])*.02-J[i+1])/2

+

The code above manipulates the point's speed. By + default, the speed is zero, which means the water will be perfectly + flat and static. When the speed changes, the y position of the point will + also change, as we described earlier.

+

I won't go into too much detail, but essentially this piece + of code just adds a bit of randomisation to the surface of the water. + If you played the game, did you notice that the water + seemed to ripple on its own? This is the code that's making that happen.

+
+ +
+

~d==~(i/9)&e>12&e<14?J[i+1]+=f/4:J[i+1]*=.97*(i<630)

+

This line of code deals with player collision and water damping. + Let's go into that with a bit more detail.

+

~d==~(i/9) This code checks if the + x position of the player - d - is in + a similar place to the given water segment. Remember, + i is our for-loop's run count, so we + can use it to get the x position of the water. Since all water + segments are layed out in a row with an equal distance between + them, it's easy to check if the player is in line with one of them.

+ +
+ +

The i/9 is a quick way of getting + the relative position of the water segment. The width of the + whole world in our game is 70 blocks. The amount of water segments + in our world is 634. So we do the calculation + 634 ÷ 70, and we get something + very close to 9. This means that + roughly 9 water segments can fit inside a block. Since our player's + position is based on the size of a block, i/9 + gives us a good approximation.

+
+ +

&e>12&e<14? Here we check if the + player's y position is near the sea level. We're not bothering + to do a proper collision check because the water will always be + hanging around the 13th row of blocks, give or take a few. + At the end of this piece of code we end the if + statement with a ?, because we now + have enough information to conclude that the player has collided + with the water.

+ +

J[i+1]+=f/4 This code makes the water + segment's y velocity match the y velocity of the player. This means that + if the player is falling, the water will splash downwards, + but if he's jumping out of the water, it'll go up instead.

+ +

:J[i+1]*=.97*(i<630) You can + probably figure out this line. Without it, any movement in + the water would repeat forever with equal force. + However, because we constantly decrease the water's speed every + frame, the surface can gradually calm down.

+
+ +
+

J[i+1]-=J[i]/25

+

Even though it may look simple, this last line is a + rather important one. It's the thing that keeps the water + hanging around the same place. If the y position of the water is too low, + the speed of the water tries increasingly + hard to push it up. When the water segment is too high, the + speed pushes it down.

+
+ +

The implementation of a water simulation may seem complicated, + but as long as you keep reminding yourself of the basic principle + that water is always trying to reach equalibrium, it should be + quite easy to implement in code.

+ +

One line I didn't discuss was the actual drawing of the water. + Let's take a minute to understand that. It's quite simple.

+ +
X.lineTo((i/9-d)*z+W/2,(J[i]+13-e)*z+H/2)
+ +

The code might look a bit overwhelming, but a large part of it + is changing the coordinates based on the camera position and scale. + Because none of that is very helpful for explanitory purposes, + I'm going to simplify the above code to this:

+ +
lineTo(i/9,J[i]+13)
+ +

Well, that's a bit easier to understand! The x position of our + water segment is i/9 to ensure that each + block holds about nine segments. The code J[i]+13 + positions the water based on its stored y position, plus the number + 13 to prevent the sea level from being too high up.

+
+ +
+

Part 4. Playable Levels

+

At the moment, the player is just navigating around a single world. + To add multiple worlds, we'll first need to check if the player has + collected all the coins and then increase the seed. We'll go + into that a bit more shortly.

+

Here is the code for checking if all the coins have been collected. See + if you can figure out how it works.

+ +
+// Make a number to count the remaining coins
+K=0
+
+#for(x=70;x--;){ // Start at 70 and count down
+#    for(y=30;y--;){ // Start at 30 and count down
+#        X.fillStyle='#'+~(Math.sin(x)*5-543)
+
+        // Increase the coin count
+        A(x,y)?r(x,y,.9):a(x,y)>-1&y<17&!k[x*y]&&(X.fillStyle='#bb0',r(x+.4,y+.4+Math.sin(_/50+x+K++)*.1,.3))
+#    }
+#}
+
+#(...)
+
+
+// Detect when there are no more coins left and reset the player's position
+!K?(d=e=1):0
+
+#requestAnimationFrame(l)
+#}
+

In the code above, we run a check to see if there are any remaining coins. + We do this by adding to a number when a coin is drawn. If the number is zero, + we know there aren't any more coins. To save a few bytes, we incremented this + number by inserting +K++ to the code that makes the + coins float.

+ +

Now for generating the next level. A key feature of generating + levels is using a seed. A seed is a code for a randomly generated + world - depending on what this "seed" is set to, the resulting + world will be different.

+ +

Let's add a seed to our level generation formula so that we can + generate different worlds every time.

+ +
+Run Code +
+// Increase the seed and clear the collected coins array
+!K?k=[Q+=d=e=1]:0
+
+#requestAnimationFrame(l)
+#}
+#X=c.getContext`2d`
+
+// Create the world seed called "Q"
+d=e=g=h=j=f=v=Q=0
+
+#k=[J=[]]
+
+#P=(e,b)=>e.keyCode==38|e.keyCode==87?g=b:e.keyCode==39|e.keyCode==68?j=b:e.keyCode==37|e.keyCode==65?h=b:0
+#onkeydown=e=>P(e,1)
+#onkeyup=e=>P(e)
+
+// Apply seed to level
+a=(x,y)=>{x=~~x+1,y=~~y;return (Math.sin(y*x*9+Q)*6)+y-15}
+#A=(x,y)=>a(x,y)>0
+
+#l()
+
+ +

Before we run the update loop, we define a new variable to represent + the seed, called Q. In our + a(x, y) function, we add this "seed" to the + formula.

+

Further up in our update loop, we clear the collected coins array + and increment our seed by 1. This will make the level automatically + switch to a different world, as our a(x, y) + function will now return different numbers.

+

The program not changes worlds! Since our game needs a few levels, + I've taken a screenshot of the first three levels for us to analyse. + This is the first level, where the seed is zero.

+
+ +

The seed is incremented by one to give us the next level. It + looks pretty good!

+
+ +

Annoyingly, the last level - where the seed is two - has given + us some problems. As the screenshot shows, we have found our first + unreachable coin, which is completely trapped under the rocks. + Further along the level there's also a very deep hole that's + impossible to jump out of.

+
+ +

As you can imagine, this was rather frustrating for me. I didn't + really want to remake my level generation formula, because I was + really happy with how it looked. The only other option was to find + a seed that actually made a playable level - and to make a formula + that could get to that seed. After a lot of testing, I realised the next + valid level was on seed 15.

+

Furtunately, because levels one and two were completely valid + anyway, it didn't take long to find a simple solution that worked.

+ +
Q = (Q * 14) + 1
+ +

At the very start of the game, the seed is set to zero, giving us + the first level. Once the level's finished, the above equation is run. + Q = (0 * 14) + 1 gives us the number 1, + which is our next valid seed. Once the player has collected all the coins, + running the formula again like Q = (1 * 14) + 1 + will give us a grand total of 15.

+ +

The formula works, let's implement that in code.

+ +
+Run Code +
+#X.fill()
+
+// Apply the formula by multiplying the seed and adding one every time
+!K?k=[Q*=14,Q+=d=e=1]:0
+
+#requestAnimationFrame(l)
+
+ +

That was a lot of work! Fortunately we've now tackled all the + complicated parts of the game, leaving us the easy job of + making the worlds look nice and adding a few final gameplay dynamics.

+
+ +
+

Part 5. Final Details

+

So far, we have created the character, added controls, figured + out some pretty good collision response, created three fully-playable + worlds and implemented a decent water simulation. All we have left + to do is just making the worlds look a bit better and adding a level + of challenge to the game.

+

For a start, let's make the blocks the right size. We can do that by + changing the line where we draw the blocks from + r(x,y,.9) to r(x,y,1).

+

We can also make a nice background by changing the code like below. + The code also includes changes to the coins' colour, walls at the side + of the level, and the addition of a temperature bar. See if you can + figure it out.

+ + +
+Run Code +
+#l=_=>{
+#W=c.width=innerWidth-16
+#H=c.height=innerHeight-16
+
+#D=d
+#E=e
+
+#g&v?f=-.18:e+=f+=.007
+#h?d-=.1:j?d+=.1:0
+#z=(W+H)/25
+
+#r=(x,y,s)=>X.fillRect((x-d)*z+W/2,(y-e)*z+H/2,z*s,z*s)
+
+// Draw two background shades
+X.fillStyle=t
+r(0,0,W)
+r(v=0,9,W)
+
+#A(d,e+.5)&!A(d,e)|A(d+.5,e+.5)&!A(d+.5,e)&&(e+.5)%1<f&&(e=E,v=1,f=0) // Ground
+#A(d,e)|A(d+.5,e)|A(d,e+.5)|A(d+.5,e+.5)|d<0|d+.5>70&&(d=D,A(d,e)|A(d+.5,e)&&(f=0,e=E)) // Wall and Ceiling
+
+#e<17&!A(d,e)&a(d,e)>-1&&(k[~~d*~~e]=1)
+
+#K=0
+#for(x=70;x--;){ // Start at 70 and count down
+#    for(y=30;y--;){ // Start at 30 and count down
+#        X.fillStyle='#'+~(Math.sin(x)*5-543)
+        // Make the coins brighter
+        A(x,y)?r(x,y,.9):a(x,y)>-1&y<17&!k[x*y]&&(X.fillStyle='#ff0',r(x+.4,y+.4+Math.sin(_/50+x+K++)*.1,.3))
+#    }
+#}
+// Walls at the edge
+r(-W,0,W)
+r(70,0,W)
+
+#X.fillStyle='#034'
+// Draw the temperature bar
+X.fillRect(0,0,W-V*W/5e3,H/9)
+#r(d,e+f,.4)
+#r(d,e+.4,.1)
+#r(d+.3,e+.4,.1)
+
+#X.fillStyle='#0698'
+#for(i=634;i-=2;X.lineTo((i/9-d)*z+W/2,(J[i]+13-e)*z+H/2)){
+#if(!_)J.push(0,0)
+#else{
+#J[i]+=J[i+1]*(i>3)
+#J[i+1]+=(J[i-1]+Math.sin(_*J[i+1])*.02-J[i+1])/2
+#~d==~(i/9)&e>12&e<14?J[i+1]+=f/4:J[i+1]*=.97*(i<630)
+#J[i+1]-=J[i]/25
+#}}
+#r(0,13,70)
+#X.fill()
+
+// Make the background golden if the player has completed all the levels
+// Reset the temperature timer at the start of every level
+// Make the temperature bar slowly get lower
+// Freeze the player if he's been playing for over a minute
+!K?Q>4?t='#ff0':k=[Q*=14,Q+=d=e=V=1]:++V>5e3?d=D:0
+
+#requestAnimationFrame(l)
+#}
+#X=c.getContext`2d`
+
+// "V" represents the progress of the temperature bar
+d=e=g=h=j=f=v=Q=V=0
+
+#k=[J=[]]
+
+// Define the default background colour
+t='#3897'
+
+#P=(e,b)=>e.keyCode==38|e.keyCode==87?g=b:e.keyCode==39|e.keyCode==68?j=b:e.keyCode==37|e.keyCode==65?h=b:0
+#onkeydown=e=>P(e,1)
+#onkeyup=e=>P(e)
+
+#a=(x,y)=>{x=~~x+1,y=~~y;return (Math.sin(y*x*9+Q)*6)+y-15}
+#A=(x,y)=>a(x,y)>0
+#l()
+
+
+ +

The temperature bar implemented above has a timer that starts + at zero and counts up every frame. When this number is larger than + 5000, it freezes the player. +

+ +
+ +

We froze the player by writing the code d=D. + This works becuase D refers the old + position of our player. By setting his active position to + match his old one, the code effectively makes him "freeze."

+
+ +

The piece of code that says Q>4?t='#ff0': + is detecting when the player has completed all the levels. Once he has, + t='#ff0' changes the background colour.

+ +

As mentioned earlier, there have also been some other updates + in the code. I haven't explained them because they are very simple. + If you're struggling to understand, the commented areas explain + all the changes in detail.

+ +

Well, believe it or not, Splash Dash has been completely made + from start to finish. It's been a long journey, but we finally + made it! However, you may be wondering why the code looks longer + than a thousand characters. That's because we haven't put it through + all the packing techniques that can get games as small as possible.

+

Let's see how small we can get it in the final section of this article.

+
+ +
+

One Last Golf

+

We've come a long way. Now it's time to compress our code into + the tiny limitation of 1024 characters. Fortunately, we've done + all the hard work. As we've coded this game, we've tried to keep + our code as simple and small as we can. The last stage is handing + our code over to a machine and see what it spits out.

+

There are a lot of JavaScript minifying tools out there. One I've + found especially good is a tool called "Terser Online," a simple + compilation of two very useful minification tools. I pasted our + JavaScript code in the box and it has given us something over + 1000 characters smaller.

+ +
+ +

How did it do that?! Essentially, terser removes anything that's + pointless. This can include whitespace, new lines, and a few other + things - that are all there for the simple purpose of making code + easier for us to read.

+ +
+ +

Whitespace is the space between our text or code that makes + it readable. Computers don't need any of this "whitespace" to + run a program, because they don't care about aesthetics. + Interestingly however, some spaces are vital for the computer + to interpret, so tools like Terser have to detect which ones + to remove.

+
+ +

But the byte size still isn't small enough! Thankfully, + Terser Online also includes a feature called "Regpack," which was + made by Siorki. Let's see how small we can go!

+ +
+ +

From that lengthly code we had at first, we've been able to + save more than 1400 characters. At the moment we're just + using JavaScript, so let's add our HTML back in like we discussed + in the outset.

+ +
<canvas id=c><script>for(p='(e,d)Q<17&!PeightO0,NNNWLr(KfoKJ=d:Ikey x,y=c./2o.fillRect(+.),4,.1e==>S(e=innerd=(i-1Styl"#+=-e)*u+HA(d5,e(d,eA)|	5)M[i-d)*u+W,(&&(=Q=>*Math.sin(+1]==e. Codel=n=>{JWwidthWidth6,HhOHO6,ogetContext`2d`,u=(W+H)5,r=,l,f)=>l,u*f,u*fC=d,F=e,g&s?f=-.18:ef.007,h?d-=.1:qd.1t,KLKs=N9,WA&!	&!)e%1<fF,s=1,f=0	)|A||d<0|d5>70C,	)f=NF)ePA)&a)>k[~~d*~~e]=1R=Nx=70;x--;)Jy=30;y--;)"+~(5x)-543A()?K,1):a()>&yPk[x*y]ff0",Kx4,y41p/50+x+R++.3));JK-W,NWK7L034",L-m*W/5e3,H/9r+f,.4rKd3,e0698",i=634;i-=2;o.lineTo(/9]+13))p?(]*>3(]02p*)-),~=~/9)&e>12&e<14?f/4:*=.97*<630-=]5):M.push(N0);KN13,70(R?++m>5e3?C:p9:T>4?t="#ff0":k=[T*=14,Tm=1],requestAnimationFrame(l)},f=g=h=q=s=u=T=m=p=Nk=[M=[]],t="#3897",S38|87?gI39|68?qI37|65?hINon down,1on upa(1+~~e,~~d,6d*e*9+T)+d5AaQ>Nl()';G=/[^!-HMR-~]/.exec(p);)with(p.split(G))p=join(shift());eval(p)</script>
+ +

Admittedly, getting the above code to fit into 1024 characters + took some tweaking. I needed to go back and change a few practices + we used earlier on and rename some variables. The hardest + part of code golfing is always the end, where even moving a letter + from one part of your code to another can save bytes.

+ +

That was a very long article, but I hope it helped you understand + how games like this are made. If there's any part you + did't understand or need help with, please tell me about it in the comments!

+
+ +
+
+

All comments are reviewed before being posted. No contact details + or other personal information will be shared.

+

If there's anything else you don't want published, please say + so in the comment.

+

Thanks for your feedback!

+ + +
+
+ +
+
+ Leave a comment + + + + +
+ + +
+
+ +
+ + +

Thanks!

+
+
+ + + + © Copyright joachimford.uk + + + + + diff --git a/articles/using_inverse_kinematics_to_animate_your_character.html b/articles/using_inverse_kinematics_to_animate_your_character.html index 807cd93..ff5140d 100644 --- a/articles/using_inverse_kinematics_to_animate_your_character.html +++ b/articles/using_inverse_kinematics_to_animate_your_character.html @@ -35,7 +35,7 @@

Using Inverse Kinematics to Animate Your Character

- Table Of Contents +

Table Of Contents

Introduction
The Transitioning Extravaganza
@@ -173,7 +173,7 @@

STEP 2: Setting the Hero's State

changeHeroJoints(activeState) - ¬draw hero ect... +# draw hero ect...

I'll explain a little of what the code above actually does. @@ -186,9 +186,9 @@

STEP 2: Setting the Hero's State

the changeHeroJoints() function. In the code below, I've given the character another state: climbing.

-
¬        hero.leg2 = Math.cos(time) * .5
-        hero.eyex = 1
-    }¬
+
#        hero.leg2 = Math.cos(time) * .5
+#        hero.eyex = 1
+#    }
 
     else if (inputState == 'climbing') {
         hero.arm1 = 1.3 + Math.sin(time / 10) * .5
@@ -217,10 +217,10 @@ 

STEP 2: Setting the Hero's State

} // update the hero joints - changeHeroJoints(activeState)¬ + changeHeroJoints(activeState) - draw hero ect...¬ +# draw hero ect...
@@ -286,15 +286,15 @@

STEP 3: Coding The Transitioning Extravaganza

if (inputState == 'walking') { bod.leg1 = Math.sin(time) * .5 - ¬(...)¬ +# (...) } else if (inputState == 'climbing') { - ¬(...)¬ +# (...) } - // return the new body from the function + // return the new body pose from the function return bod } @@ -310,17 +310,17 @@

STEP 3: Coding The Transitioning Extravaganza

transition = 0 -¬function update() { +function update() { requestAnimationFrame(update) - (...)¬ +# (...) // get old and new expected body positions const oldBody = changeHeroJoints(oldState) - const newBody = changeHeroJoints(activeState)¬ + const newBody = changeHeroJoints(activeState) - draw hero ect...¬ +# draw hero ect...

At the moment, the code shown above won't do anything. If you look @@ -330,7 +330,14 @@

STEP 3: Coding The Transitioning Extravaganza

The code is very incomplete, so let's finish it off. Have a read of the comments if you need help.

-
// create a new array representing all the moving parts
+
+#function createBody() {
+#    return {
+#        (...)
+#    }
+#}
+
+// Create a new array representing all the moving parts
 // in our hero's body. This is important
 const arr = [
     'arm1',
@@ -341,7 +348,9 @@ 

STEP 3: Coding The Transitioning Extravaganza

'eyey', 'body', 'ang' -]¬ +] + +const hero = createBody() oldState = '' activeState = 'walking' @@ -349,7 +358,7 @@

STEP 3: Coding The Transitioning Extravaganza

function update() { requestAnimationFrame(update) - (...)¬ +# (...) const oldBody = changeHeroJoints(oldState) const newBody = changeHeroJoints(activeState) @@ -376,10 +385,10 @@

STEP 3: Coding The Transitioning Extravaganza

} // set straight away if not transitioning if (!transition) - hero = newBody¬ + hero = newBody - draw hero ect...¬ +# draw hero ect...

Well, what is the code doing? First, we created a new array - @@ -407,7 +416,7 @@

STEP 3: Coding The Transitioning Extravaganza

but for those who like minor details (like me), make this one small edit. No need to worry about figuring out the maths.

-
¬hero[arr[i]] = oldPart + (dist * ¬(.5 + Math.cos(transition * Math.PI + Math.PI) * .5)¬)
+
hero[arr[i]] = oldPart + (dist * (.5 + Math.cos(transition * Math.PI + Math.PI) * .5))

To discover more pointless details from Io's Mission, @@ -618,14 +627,30 @@

Conclusion

</html>
+ +
+
+

All comments are reviewed before being posted. No contact details + or other personal information will be shared.

+

If there's anything else you don't want published, please say + so in the comment.

+

Thanks for your feedback!

+ + +
+
+
Leave a comment - - - + + +
+ + +
@@ -638,8 +663,8 @@

Conclusion

© Copyright joachimford.uk - - + + - + \ No newline at end of file diff --git a/content/anthrophobia_fear_of_crowds.html b/content/anthrophobia_fear_of_crowds.html index 73a94fd..33e9327 100644 --- a/content/anthrophobia_fear_of_crowds.html +++ b/content/anthrophobia_fear_of_crowds.html @@ -34,7 +34,7 @@

Anthrophobia

@@ -62,39 +62,40 @@

Anthrophobia

-
¬const yDrop = cvs.height / 5
+
#const yDrop = cvs.height / 5
 
-for (let i = 2; i --;) {¬
-    const x = cvs.width / 2 - Math.sin(time + i * Math.PI)¬ * cvs.height / 4 - 10
-    ¬let y = cvs.height / 2 + Math.cos(time + i * Math.PI)¬ * cvs.height / 4 - 10
+#for (let i = 2; i --;) {
+    const x = cvs.width / 2 - Math.sin(time + i * Math.PI) * cvs.height / 4 - 10
+    let y = cvs.height / 2 + Math.cos(time + i * Math.PI) * cvs.height / 4 - 10
 
-    if (y > cvs.height / 2) y = cvs.height / 2
-    y += yDrop
+    #if (y > cvs.height / 2) y = cvs.height / 2
+    #y += yDrop
 
-    ¬const swing = Math.sin(time + i * Math.PI)¬
-    let rise = swing
-    if (rise < 0) rise = 0
-    y -= rise * 40
+ const swing = Math.sin(time + i * Math.PI) + #let rise = swing + #if (rise < 0) rise = 0 + #y -= rise * 40

Of course, the manner of this walk can be changed by stretching its movement or by giving it more characteristic bops and drags. The example below really shows how loose you can be with the whole thing. For me, - this walk is much better.

+ this walk is much better. Probably not so good for the heels, + but more realistic to look at, that is.

-
¬for (let i = 2; i --;) {
-    ¬const x = -Math.sin(time + i * Math.PI + Math.sin(time + i * Math.PI + 2) * .3)¬ * 100
-    ¬let y = Math.cos(time + i * Math.PI + Math.sin(time + i * Math.PI + 2))¬ * 100
+
#for (let i = 2; i --;) {
+    const x = -Math.sin(time + i * Math.PI + Math.sin(time + i * Math.PI + 2) * .3) * 100
+    let y = 30 + Math.cos(time + i * Math.PI + Math.sin(time + i * Math.PI + 2)) * 130
 
-    if (y > 0) y = 0
+    #if (y > 0) y = 0
 
-    ¬const swing = Math.sin(time + i * Math.PI + Math.sin(time + i * Math.PI + 3.1))¬
-    let rise = swing
-    if (rise < 0) rise = 0
-    y -= rise * 40
+ const swing = Math.sin(time + i * Math.PI + Math.sin(time + i * Math.PI + 3.1)) * 1.3 + #let rise = swing + #if (rise < 0) rise = 0 + #y -= rise * 40

In the above example I tried to make the walk a bit comical, so you'd probably want to tweak it based on @@ -107,14 +108,29 @@

Anthrophobia

contact me if you have any more ideas for experiments!

+
+
+

All comments are reviewed before being posted. No contact details + or other personal information will be shared.

+

If there's anything else you don't want published, please say + so in the comment.

+

Thanks for your feedback!

+ + +
+
+
Leave a comment - - - + + +
+ + +
@@ -127,8 +143,8 @@

Anthrophobia

© Copyright joachimford.uk - - + + - + \ No newline at end of file diff --git a/content/canvas_craft.html b/content/canvas_craft.html index 4d57e5e..6be53c4 100644 --- a/content/canvas_craft.html +++ b/content/canvas_craft.html @@ -57,14 +57,29 @@

Try it today at .

+
+
+

All comments are reviewed before being posted. No contact details + or other personal information will be shared.

+

If there's anything else you don't want published, please say + so in the comment.

+

Thanks for your feedback!

+ + +
+
+
Leave a comment - - - + + +
+ + +
@@ -77,8 +92,8 @@

Try it today at Change Theme © Copyright joachimford.uk - - + + - + \ No newline at end of file diff --git a/content/extra/anthrophobia_fear_of_crowds/2.html b/content/extra/anthrophobia_fear_of_crowds/2.html index d0f8948..130e4bf 100644 --- a/content/extra/anthrophobia_fear_of_crowds/2.html +++ b/content/extra/anthrophobia_fear_of_crowds/2.html @@ -37,7 +37,7 @@ } else camX = camY = 0 - const speed = (time * 53 + Math.cos(time * 2 - 1) * 20) * scale + const speed = (time * 53 + Math.cos(time * 2 - .94) * 21) * scale for (let i = 0; i < 3; i ++) { const xPos = Math.sin(i * i * 4) * cvs.width @@ -64,11 +64,11 @@ ctx.translate(cvs.width / 2, cvs.height / 2 + yDrop) for (let i = 2; i --;) { const x = -Math.sin(time + i * Math.PI + Math.sin(time + i * Math.PI + 2) * .3) * 100 - let y = Math.cos(time + i * Math.PI + Math.sin(time + i * Math.PI + 2)) * 100 + let y = 30 + Math.cos(time + i * Math.PI + Math.sin(time + i * Math.PI + 2)) * 130 if (y > 0) y = 0 - const swing = Math.sin(time + i * Math.PI + Math.sin(time + i * Math.PI + 3.1)) + const swing = Math.sin(time + i * Math.PI + Math.sin(time + i * Math.PI + 3.1)) * 1.3 let rise = swing if (rise < 0) rise = 0 y -= rise * 40 diff --git a/content/infinite_zoom.html b/content/infinite_zoom.html index 9edf8c5..d1b13f5 100644 --- a/content/infinite_zoom.html +++ b/content/infinite_zoom.html @@ -32,7 +32,7 @@

Infinite Zoom

@@ -77,14 +77,30 @@

Infinite Zoom

that represented the icon. This meant I could draw double the distance without the performance suffering too much.

+ +
+
+

All comments are reviewed before being posted. No contact details + or other personal information will be shared.

+

If there's anything else you don't want published, please say + so in the comment.

+

Thanks for your feedback!

+ + +
+
+
Leave a comment - - - + + +
+ + +
@@ -97,8 +113,8 @@

Infinite Zoom

© Copyright joachimford.uk - - + + - + \ No newline at end of file diff --git a/content/ios_mission.html b/content/ios_mission.html index 37db375..74303a8 100644 --- a/content/ios_mission.html +++ b/content/ios_mission.html @@ -56,7 +56,7 @@

Io's Mission

@@ -104,26 +104,42 @@

Things I Learned

You can read more about how I made Io's Mission - in the articles below. If you're still interested, feel free to take a - look at the source code by following the link.

+ in the articles below. You can also take a + look at the source code if you're interested!

- + +
+
+

All comments are reviewed before being posted. No contact details + or other personal information will be shared.

+

If there's anything else you don't want published, please say + so in the comment.

+

Thanks for your feedback!

+ + +
+
+
Leave a comment - - - + + +
+ + +
@@ -136,8 +152,8 @@

Things I Learned

© Copyright joachimford.uk - - + + - + \ No newline at end of file diff --git a/content/ray_casting_blocks.html b/content/ray_casting_blocks.html index 496476d..21afa4f 100644 --- a/content/ray_casting_blocks.html +++ b/content/ray_casting_blocks.html @@ -33,7 +33,7 @@

Ray Casting Blocks

@@ -238,14 +238,30 @@

"It's surprisingly short, actually"

hard to overcome the initial limitations, but if you can do it it's a great trick to have up your sleeve!

+ +
+
+

All comments are reviewed before being posted. No contact details + or other personal information will be shared.

+

If there's anything else you don't want published, please say + so in the comment.

+

Thanks for your feedback!

+ + +
+
+
Leave a comment - - - + + +
+ + +
@@ -258,8 +274,8 @@

"It's surprisingly short, actually"

© Copyright joachimford.uk - - + + - + \ No newline at end of file diff --git a/content/snakes_and_ladders.html b/content/snakes_and_ladders.html index 73f34f3..788ed0d 100644 --- a/content/snakes_and_ladders.html +++ b/content/snakes_and_ladders.html @@ -34,7 +34,7 @@

Snakes And Ladders

@@ -60,14 +60,30 @@

Snakes And Ladders

Check Out the Source Code
+ +
+
+

All comments are reviewed before being posted. No contact details + or other personal information will be shared.

+

If there's anything else you don't want published, please say + so in the comment.

+

Thanks for your feedback!

+ + +
+
+
Leave a comment - - - + + +
+ + +
@@ -97,8 +113,8 @@

Hope41

© Copyright joachimford.uk - - + + diff --git a/content/squirtcopter.html b/content/squirtcopter.html index 281eda7..b147a18 100644 --- a/content/squirtcopter.html +++ b/content/squirtcopter.html @@ -56,7 +56,7 @@

Squirtcopter

@@ -82,14 +82,30 @@

Squirtcopter

Check Out the Source Code
+ +
+
+

All comments are reviewed before being posted. No contact details + or other personal information will be shared.

+

If there's anything else you don't want published, please say + so in the comment.

+

Thanks for your feedback!

+ + +
+
+
Leave a comment - - - + + +
+ + +
@@ -102,8 +118,8 @@

Squirtcopter

© Copyright joachimford.uk - - + + - + \ No newline at end of file diff --git a/content/the_mitigator.html b/content/the_mitigator.html index ccbfc01..0b2de71 100644 --- a/content/the_mitigator.html +++ b/content/the_mitigator.html @@ -33,7 +33,7 @@

The Mitigator

@@ -54,14 +54,30 @@

The Mitigator

Check Out the Source Code
+ +
+
+

All comments are reviewed before being posted. No contact details + or other personal information will be shared.

+

If there's anything else you don't want published, please say + so in the comment.

+

Thanks for your feedback!

+ + +
+
+
Leave a comment - - - + + +
+ + +
@@ -74,8 +90,8 @@

The Mitigator

© Copyright joachimford.uk - - + + - + \ No newline at end of file diff --git a/experiments.html b/experiments.html index d06de9d..0023bff 100644 --- a/experiments.html +++ b/experiments.html @@ -44,7 +44,7 @@

Ray-casting Blocks

A 3D city run experiment using ray-casting graphics. Watch the camera run though a vivid world of blocks. - This experiment mobile friendly. + This experiment is mobile friendly. @@ -71,8 +71,8 @@

Anthrophobia

© Copyright joachimford.uk - - + + \ No newline at end of file diff --git a/games.html b/games.html index acc5ab6..193e87b 100644 --- a/games.html +++ b/games.html @@ -28,7 +28,7 @@
- +

Squirtcopter

@@ -39,7 +39,7 @@

Squirtcopter

- +

Io's Mission

@@ -51,7 +51,7 @@

Io's Mission

- +

Snakes And Ladders

@@ -62,7 +62,7 @@

Snakes And Ladders

- +

The Mitigator

@@ -78,8 +78,8 @@

The Mitigator

© Copyright joachimford.uk -
- + + diff --git a/images/back.svg b/images/back.svg new file mode 100644 index 0000000..5b4701c --- /dev/null +++ b/images/back.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/svg/bulb.svg b/images/bulb.svg similarity index 100% rename from svg/bulb.svg rename to images/bulb.svg diff --git a/images/dwitter.svg b/images/dwitter.svg new file mode 100644 index 0000000..f93768b --- /dev/null +++ b/images/dwitter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/github.svg b/images/github.svg new file mode 100644 index 0000000..1777d43 --- /dev/null +++ b/images/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/info.svg b/images/info.svg new file mode 100644 index 0000000..1761d33 --- /dev/null +++ b/images/info.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/images/ios_mission_big.png b/images/ios_mission_big.png index 1b0c69c..a02425c 100644 Binary files a/images/ios_mission_big.png and b/images/ios_mission_big.png differ diff --git a/images/itch.svg b/images/itch.svg new file mode 100644 index 0000000..c9fb288 --- /dev/null +++ b/images/itch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/svg/mail.svg b/images/mail.svg similarity index 100% rename from svg/mail.svg rename to images/mail.svg diff --git a/svg/play.svg b/images/play.svg similarity index 100% rename from svg/play.svg rename to images/play.svg diff --git a/images/x.svg b/images/x.svg new file mode 100644 index 0000000..c9fb288 --- /dev/null +++ b/images/x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/youtube.svg b/images/youtube.svg new file mode 100644 index 0000000..284f645 --- /dev/null +++ b/images/youtube.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/index.html b/index.html index eda6030..815ea81 100644 --- a/index.html +++ b/index.html @@ -25,8 +25,11 @@ } canvas { + display: inline-block; width: 100%; - height: 100%; + position: fixed; + top: 60px; + background-color: #000; } .main { @@ -51,15 +54,18 @@
+ +
- + + \ No newline at end of file diff --git a/news/splash_dash.png b/news/splash_dash.png new file mode 100644 index 0000000..2a86058 Binary files /dev/null and b/news/splash_dash.png differ diff --git a/svg/github.svg b/svg/github.svg deleted file mode 100644 index 2778e27..0000000 --- a/svg/github.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file