Let’s Program A JavaScript Game 11: This Post Isn’t About Malware, Honest

Let’s Get Dangerous

We have a cycle for the player and an endlessly scrolling screen of platforms for them to jump along. The only piece we’re missing are the deadly virus enemies for the player to avoid.

Viruses are going to be implemented more or less the same as platforms. We’ll have an array of viruses that scroll that to the left. When a virus has moved entirely off screen we drop it from the front of the list and then pin a new virus to the back of the list. In the future we will randomize the location and timing of new viruses to make things a little less predictable but for now a couple viruses marching in a steady line will let us test everything we need to test.

Writing Simple Enemies

The code for the virus obstacles is almost identicle to the code for the platforms. Generate an array of objects, move them a little to the left every frame and check for collisions with the player. So nothing here should be especially mind blowing.

First up, we generate the original list of viruses at the top of code by putting this code right after the platform setup code:

var viruses = new Array();
var virusStartingOffset = 1000;
var maxVirusCount = 2;
var maxVirusGap = 400;
var virusWidth = 50;
var virusHeight = 50;

//Generate starting viruses (all off screen at first)
for(i = 0; i < maxVirusCount; i++){
   var newVirus = new Object();
   newVirus.x = virusStartingOffset + i * (virusWidth + maxVirusGap);
   newVirus.y = 200;
   newVirus.width = virusWidth;
   newVirus.height = virusHeight;
   viruses[i] = newVirus;
}

You also need to include this image related code somewhere so we can draw the viruses. I suggest putting it right after the cycle image code:

var virusImage = new Image();
virusImage.src = "virus.png";

Next up is adding virus code to the updateGame method. Just drop this in right after the big block of platform code:

//Virus logic
for( i = 0; i < viruses.length; i++){
   //Have all viruses move towards the player
   viruses[i].x-=5;
   
   //See if the player is grazing this virus
   if(intersectRect(viruses[i], grazeHitbox)){
      grazeCollision = true;
   }

   //See if the player has had a lethal collision with this virus
   if(intersectRect(viruses[i], deathHitbox)){
      deathCollision = true;
   }
}

//Remove viruses that have scrolled off screen and create new viruses to take their place
if(viruses[0].x + viruses[0].width < 0){
   //Drop first virus by shifting entire list one space left
   for( i = 0; i < viruses.length-1; i++){
      viruses[i] = viruses[i+1];
   }

   //Add new platfrom to end of list by placing it a certain distance after second to last platfor
   var newVirus = new Object();
   newVirus.x = viruses[viruses.length-2].x + virusWidth + maxVirusGap;
   newVirus.y = 200;
   newVirus.width = 50;
   newVirus.height = 50;
   viruses[viruses.length-1] = newVirus;
}

And finally we draw the viruses by adding this simple loop somewhere in our drawScreen method

//Draw viruses
for(i = 0; i < viruses.length; i++){
   context.drawImage(virusImage, viruses[i].x, viruses[i].y);
}

That’s it. You now have a couple of virus obstacles scrolling across the screen along with the platforms. You can jump near them and into them and watch your “graze” and “death” collision status change. These collisions won’t actually do anything yet, but just keeping track of them is the first step towards building a real game-over and bonus point system.

Both “Graze Collision” and “Death Collision” are true, showing we got just a little too close during this test

Both “Graze Collision” and “Death Collision” are true, showing we got just a little too close during this test

Why We Did What We Did

This entire branch of code was mostly just copy pasting the platform code and then renaming the variables, so no big ideas need to be explained. But there was one tiny design decisions that I think deserve a little extra analysis: Why did we give each virus a specific height and width instead of just using the height and width of the global virus image?

Two big reasons.

Reason one: Our collision detection function expects to be given two rectangle like objects that know their own x position, y position, width and height. Giving every virus a copy of this information means we can drop them directly into the collision detection function.

Reason two: We may not want our viruses to always be the same size as their picture. In the future we might decide that we want the virus’s collision box to be smaller than the virus picture in order to make collisions easier to avoid. So by keeping the virus’s height and weight independent form the size of the graphics we draw we give ourselves the freedom to make major changes in the future without breaking anything.

Next Up: Game-Over

With most of the basic game mechanics in place the next thing to do is finally let player’s lose, which is a surprisingly big tasks since it means we will have to introduce ideas like “game states” and “multiple possible screen renderers”.

But don’t worry, it’s actually a pretty simple once you get into it. And it involves flowcharts! Everyone loves flowcharts!

Gengo Girls #41: You Can’t Make Me

Gengo Girls #41: You Can't Make Me

Remember, there is no future tense in Japanese so you’re going to be using the same negative verbs for both describing the present and the future. Depending on the sentence you put it in “benkyou shimasen” can mean “I’m not studying right now”, “I don’t study in general” or “I am not going to study in the future”.

Vocabulary

出来る = できる = to be able to do something

Transcript

言語ガールズ #41

You Can’t Make Me

Blue: It’s important to know how to make negative verbs so you can say things like “I will not go” instead of just “I will go”.

Blue: Making a polite negative verb is easy. Just replace the ます with ません.

Blue: So “I will not go” is 行きません.

Yellow: And “I do not study” is 勉強しません.

Blue: Could you try to choose a more responsible sounding example?

Yellow: 出来ません!

Gengo Girls #40: Negative On That Positive Reinforcement

Gengo Girls #40: Negative On That Positive Reinforcement

Don’t you just love that feeling you get when a new idea finally clicks?

Vocabulary

= ふく = clothes

きれい = pretty

高い = たかい = expensive; tall, high

Transcript

言語ガールズ #40

Negative On That Positive Reinforcement

Yellow: We’re practicing our 日本語 at the mall?

Blue: There are lots of things to look at and talk a bout here. Like that clothing shop over there.

Yellow: あの服はきれいです

Blue: 高いです

Yellow: Oh! I finally get it!

Yellow: Your sentence didn’t need a subject because it was obvious we were both talking about the same piece of clothing.

Yellow: You should celebrate this learning moment by buying me あの服.

Blue: You overestimate both my allowance and my generosity.

Gengo Girls #39: If You Know What I Mean

Gengo Girls #39: If You Know What I Mean

いい天気ですね is a lot like saying “It’s nice outside” and trusting in your listener to figure out that by “it” you mean “today’s weather”.

As a side note, it’s not that uncommon to hear someone say the full 今日はいい天気ですね. Just because you CAN leave out an obvious topic word doesn’t mean you HAVE to leave it out (although the Japanese usually do).

Vocabulary

今日 = きょう = today

Transcript

言語ガールズ #39

If You Know What I Mean

Yellow: If marks the main topic of a sentence, how come some sentences don’t have a ?

Yellow: Like いい天気ですね.

Blue: If your listener already knows what the main topic is going to be you don’t have to actually include it in the sentence.

Blue: So you could say 今日はいい天気ですね.

Blue: But you don’t have to include the 今日は because people know that you’re probably talking about today’s weather.

Yellow: So the better you are at guessing my topics the less 日本語 I have to actually use?

Blue: Don’t use this as an excuse to skip out on memorizing new vocabulary!

 

Gengo Girls #38: Ha Ha Ha

Gengo Girls #38: Ha Ha Ha

 

Japanese has a lot of spoken grammar markets. They use to mark sentences as questions. They use to mark topics. And they use things like and to mark various other important bits of information that we’ll talk about much later.

Which is good news for us because it can make analyzing complex sentences much simpler. Even in a sentence with dozens of adjectives and adverbs and prepositional phrases you can still depend on to point you to the main subject. Most of the time…

Transcript

言語ガールズ #38

Ha Ha Ha

Blue: Let’s talk about the reason that sometimes sounds like “wa” even though it’s usually pronounced “ha”.

Yellow: Is the reason: “Because 日本語 is confusing”?

Blue: In 日本語 every sentence has a main topic, subject or theme.

Blue: That theme is marked by putting a right after it, and it’s these topic marking that sound like “wa”.

Blue: So if you wanted to talk about cats your sentence would start off with “猫は”.

Yellow: Like in 猫は可愛いです.

Blue: Now you can figure out the topic of any sentence by just looking for whatever word comes before the .

Yellow: That’s almost useful enough to make up for one letter having two sounds.

 

Announcement: Immortals Should Try Harder Beta Release

As a child I promised myself I was going to program video games when I grew up. It’s actually why I got into computer science in the first place. And while I have since learned that games aren’t the only cool thing you can do with code I still have a soft spot in my heart for that childhood dream.

Which is why I’m happy to announce the test release of Immortals Should Try Harder, a comedy fantasy RPG that I’ve been slowly but steadily working on in my free time.

I think it turned out pretty decent for a hobby game with no actual budget, so if you have a few hours to burn why not give it a try? And please don’t hesitate to contact me with bug reports, game criticisms or any other sort of comment or concern.

Gengo Girls #37: Victor’s Humility

Gengo Girls #37: Victor's Humility

 

Using “suru” to turn a noun into a verb isn’t that different from how we use “to do” in English. For example, you would never say “That man can math”, you would say “That man can do math”.

The big difference here is that “to do” comes before the noun but “suru” and “shimasu” comes after.

Vocabulary

する = to do

来る = くる = to come

勉強 = べんきょう = study (noun)

勉強する = to study (verb)

Transcript

言語ガールズ #37

Victor’s Humility

Yellow: I found two verbs that don’t follow the normal polite conjugation rules.

Blue: You must be talking about する and 来る.

Blue: The polite form of する is します and the polite form of 来る is きます.

Yellow: Oh… I guess you already knew about them.

Blue: する is especially important because it can be used to turn certain nouns into verbs.

Blue: Like turning 勉強 into 勉強する.

Yellow: I was really hoping I had learned something before you this time.

Blue: It’s not a competition.

Yellow: You’re only saying that because you’re winning.

 

Gengo Girls #36: I Spy

Gengo Girls #36: I Spy

Take a few minutes to look at each of these three verbs and make sure you understand how to make them polite. Two of them use the “change the last sound to ‘i’ and add ‘masu’” rule and one uses the “replace final ‘ru’ with ‘masu’” rule.

Also we’re kind of cutting corners here. Very few people would actually say something like “That person walks”. They would be more likely to say “That person is walking”. But all we know for now are simple verbs so that’s what we’re going to practice.

Vocabulary

遊ぶ = あそぶ = to play

= ひと = person

歩く = あるく = to walk

寝る = ねる = to sleep

Transcript

言語ガールズ #36

I Spy

Blue: Let’s go to the park for some 日本語 practice.

Yellow: OK.

Blue: 犬は遊びます

Blue: あの人は歩きます

Blue: Aren’t you going to say anything?

Yellow: 私は寝ます 

Let’s Program A JavaScript Game 10: Letting The World Pass By

A World, Or The Illusion Of A World?

Our end goal is to let the player spend two or three minutes jumping from platform to platform and avoiding viruses. But how do we go about accomplishing this?

My first instinct was to build a hundred or so platforms and position them around the game world. Then for each frame of the game we would check whether or not the player had hit any of the platforms and finish up by drawing any platforms that were currently on screen.

And this would work. Keeping track of a few hundred objects and possible object collisions is no sweat for a modern computer.

But there are a few reasons that keeping track of an entire world of platforms might not be such a great idea for this particular game:

  • We only have room for two or three platforms on the screen at one time. Keeping track of the extras is a waste (even if it is one we can afford)
  • The player can never move backwards so we don’t have to keep track of platforms we have already passed.
  • Building an entire world ahead of time would make it impossible to adjust the game for players who are doing well. Players who get a lot of graze points should start finding small platforms and big gaps much earlier than players who don’t. Otherwise they might win the game through graze points without ever hitting any difficult jumps.

So instead of creating an entire world of platforms I’m only going to create a list of 5 platforms. When a platform gets left behind by the player I will delete it and then generate a new platform to add to the end of the list.

To the player this will still look like a complete world with hundreds of platforms. There will always be two or three platforms on screen and there will always be a new platform waiting for them as they move to the right. The player will never know that platforms are disappearing behind them or that they are popping into existence mere seconds before they are needed.

All Motion Is Relative

Now that we’ve decided how to build the platforms we also have to decide how we’re going to scroll the screen. After all, the entire game is based around the player constantly moving to the right.

You might think that if the game is about the player moving to the right the only solution is to make the player actually move to the right, maybe by increasing their x position by a constant amount every turn. But if we do that the player would eventually drive right off the right side of the screen, which would be bad.

So instead of moving the player forwards we’re going to move the entire world backwards. It’s not even that hard to do. Every frame we just iterate through out list of five platforms and adjust their x position backwards.

Infinite Platforms Demo

To put this all into play we need to make the following changes to our code:

  • Create a list of platforms
  • Populate the list with five starter platforms
  • Move those platforms to the left during each frame
  • Check for player collision with all five platforms during each frame
  • Delete platforms that scroll entirely off the screen
  • Add a new platform to the end of the list every time one gets deleted from the front
  • Draw any platforms that happen to be on screen

Let’s start by creating and populating the platform list. This code goes before any function declarations and replaces our old testObject1 and testObject2 declarations:

var platforms = new Array();
var maxPlatformCount = 5;
var maxPlatformLength = 200;
var maxPlatformGap = 150;

//Generate starting platforms
for(i = 0; i < maxPlatformCount; i++){
   var newPlatform = new Object();
   newPlatform.x = i * (maxPlatformLength + maxPlatformGap);
   newPlatform.y = 350;
   newPlatform.width = maxPlatformLength;
   newPlatform.height = 20;
   platforms[i] = newPlatform;
}

Now we need to go down to our updateGame method and replace our testObject collission code with this:

var feetHitbox = getFeetHitbox(player.x, player.y);

//Platform logic
for( i = 0; i < platforms.length; i++){
   //Create illusion of player moving forward by scrolling all platforms backwards
   platforms[i].x-=5;

   //See if the player has landed on this platform
   if(intersectRect(platforms[i], feetHitbox)){
      feetCollision = true;
      player.yVel=0;
      player.y=platforms[i].y-48;
      player.onGround=true;
   }
}

Few things to notice: We now create one set of player hitboxes per frame and then use them multiple times inside the loop. This is slightly more efficient than creating the hitboxes multiple times inside the loop.

We are also using the loop to both update the position of each platform and check for platform collisions. This is slightly more efficient than having two seperate loops: one for updating position and one for checking for collisions.

Now we can draw the platforms by replacing the testObject related drawing code in drawScreen with this:

//Draw platforms
context.fillStyle = '#008800';
for(i = 0; i < platforms.length; i++){
   context.fillRect(platforms[i].x, platforms[i].y, platforms[i].width, platforms[i].height);
}

Now if you try to actually run the game at this point you should see five moving platforms… but as soon as they all scroll past the screen you get stuck in an endless loop of falling of the screen again and again and again. Let’s fix that by going back up to updateGame and adding in code to remove old platforms and add new ones in. This particular code should come right after the end of the foreach loop we used for updating the platforms:

//Remove platforms that have scrolled off screen and create new platforms to take their place
if(platforms[0].x + platforms[0].width < 0){
   //Drop first platform by shift entire list one space left
   for( i = 0; i < platforms.length-1; i++){
      platforms[i] = platforms[i+1];
   }

   //Add new platfrom to end of list by placing it a certain distance after second to last platform
   var newPlatform = new Object();
   newPlatform.x = platforms[platforms.length-2].x + maxPlatformLength + maxPlatformGap;
   newPlatform.y = 350;
   newPlatform.width = maxPlatformLength;
   newPlatform.height = 20;
   platforms[platforms.length-1] = newPlatform;
}

And that’s that. You now have an infinitely scrolling world of platforms and gaps. It’s a pretty boring world, to be honest, and we’ll definitely want to mix things up a little before giving this to an actual player. But as a piece of proof of concept code this all pretty good.

Jump. Jump. Jump. Forever.

Jump. Jump. Jump. Forever.

Feeling Lost? Here’s A Map

My last few posts have included a lot of instructions to “delete this” and “insert that” and I’ll admit that can be vague and confusing. So here’s a snapshot of my code right now. Follow the link to try it out or save it to your computer to read through my code. Just put it in the same folder as the cycle.png image and it should run just fine.

Things Are About To Get More Dangerous

With platforms live and moving the next major feature to work on are the deadly viruses that the player is supposed to avoid and graze. So tune in again next time to see how that works out.

Gengo Girls #35: The Future Is Now

Gengo Girls #35: The Future Is Now

At first the idea of not having a specific future tense might seem confusing, but look at it this way: That’s one less conjugation for you to learn.

And it’s not like it’s really that strange of an idea. In English both “I’m going to the store” and “I’m going to next week’s concert” have the same verb but I bet you didn’t have any trouble at all telling which one is happening now and which one is happening in the future.

Transcript

言語ガールズ #35

The Future Is Now

Blue: Did you know that 日本語 doesn’t have a future tense?

Yellow: Really?

Blue: 私は行きます can mean both “I go” and “I will go”.

Yellow: Then how do I tell the difference between present and future verbs?

Blue: From context.

Blue: If you say 行きます while walking out the door, that would mean “I go”.

Blue: But if we were talking about a concert happening next week and you said 行きます that would mean “I will go”.

Yellow: A concert? Of course I’m going!

Blue: There is no concert. It was just an example.

Yellow: Awww…