Jelly Blobs of Doom¶
For this assignment, we'll implement a delightful game that Alice Zhou and Jacqueline Young created for CS 111 in Python, and Alice adapted to JavaScript. I rewrote and refactored the game, but most of the credit goes to Alice, and I'm grateful for her work on this.
The instructions for the game are quite simple:
- Move the mouse to control the blue blob
- Eat blobs that are smaller than you in order to grow
- Avoid bigger blogs or you will shrink
- Goals
- Reference Solution
- TL;DR
- Blobs
- Object Location
- Checking Location
- Intersection
- Testing Blobs
- Player Object
- The Player Object
- Enemies
- Testing updateLocation
- Testing Intersection
- jQuery Animation
- Animation and our Model
- Testing
- Mouse Movement
- Random Stuff
- Automatic stuff
- Starting the Game
- Stopping the Game
- Hints
- Element CSS
- Putting Elements Off-stage
- Collision Methods
- Modularity
- Getting Started
- How to turn this in
- Tutors/Graders
Goals¶
The educational goals for this assignment are primarily to use OOP and inheritance in your implementation.
There are three entities that you will have to implement:
- Blobs
- Enemies
- the Player
Both Enemies and the Player are subtypes of Blobs.
In addition to implementing these three objects, you'll define a mouse-movement event handler and you'll work with jQuery animations.
Reference Solution¶
Here's the obfuscated reference solution
Here's the obfuscated reference solution w/ debugging turned
on The debugging version is exactly
the same code but because debug=true
is in the URL, the behavior is
slightly different. Enemies go from edge to edge and don't disappear,
rather than start and ending off-screen.
Feel free to copy the HTML and CSS files. Clear out the code in the obfuscated files. You will implement these files:
Blob.js
implements theBlob
classPlayer.js
implements thePlayer
class, which inherits fromBlob
Enemy.js
implements theEnemy
class, which also inherits fromBlob
game.js
attaches event handlers, puts all the pieces together, and implements the game.
Your solution will also load two files that I supply, namely:
https://cs.wellesley.edu/~cs204/assignments/jelly/random.js
which implements three useful functions involving pseudo-randomness.https://cs.wellesley.edu/~cs204/assignments/jelly/testing.js
which implements some useful functions to test your code.
TL;DR¶
Warning, this is another difficult assignment. But I have faith that you'll be able to do it, and, besides, it's a fun game. In total, my solution has about 270 lines in the four JavaScript files, including blank lines and comments.
Another note: this assignment description is long, and sometimes suffers from the TL;DR phenomenon. I understand that you're eager to get started coding, particularly since you know there's a lot of coding, but time spent reading and mentally preparing will pay off with less time spent coding. I'll tell you what my wife tells my son when he's eager to start cooking: first read the whole recipe because sometimes earlier steps make more sense when you know where the recipe is going. Then, you can start cooking, reading the recipe again step by step, while you cook. So, with this long recipe, I suggest you read it all first, and then read it again, step by step, as you code.
Blobs¶
Blobs have instance variables to keep track of
- their color (though it never changes)
- their radius (the player's size dynamically changes, but enemy sizes don't)
- the x and y location of their center (changes for both subclasses)
- the DOM element that corresponds to the blob.
The DOM element is quite simple:
<div class="circle"></div>
The CSS is where all the magic occurs:
.circle {
border-radius: 50%;
position: absolute;
/* all the following will be overridden in real blobs */
background-color: gray;
width: 200px;
height: 200px;
top: 0px;
left: 0px;
}
Because the border-radius is 50% (of the width of the DIV), the DIV will
be a circle. The position:absolute
will position the DIV on the page
(you could have a smaller playing area, but we'll use the whole
document).
I implemented the constructor to have two arguments, color
and
radius
; I also implemented the following methods. Some might be
unnecessary, but I wanted to be complete.
addToGame
which adds the blob to some container (usually thebody
)setDOM
creates a DOM element (thediv
described above) and stores it in an instance variablesetColor
which sets thecolor
instance variable and also needs to update the DOM element's background-color property.setRadius
which sets the appropriate instance variable(s) and also needs to update the DOM element's width, height, left and top properties.getColor
returns the current color.getDOM
returns the DOM element stored in the instance variablegetDiameter
returns the valuegetRadius
returns the valuegetX
andgetY
return the x,y coordinates of the centersetX
andsetY
change the x,y coordinates of the center and also update the position of the DOM element by setting left or top.
You'll notice that there's no setDiameter
method. Setting the
diameter to any odd number means that the radius is not an
integer. (If you set the diameter to 3, the radius is 1.5.)
Non-integers can be ugly and awkward sometimes, particularly when we
are synchronizing them with CSS. So I eliminated that method. You can
implement it if you want, but stick to even integer values.
Object Location¶
The math involved with the object's (x,y) location and the DOM element's CSS positioning (left,top) is not complicated, but students often get confused, so I often end up drawing the following figure:
The following should always be true:
left+radius == x top+radius == y
Given that figure, ask yourself:
- if the caller uses
setX
to change the blob's location, what values have to change and how? - if the caller uses
setRadius
to make the blob bigger, while keeping the center (x,y) unchanged (that is, the blob doesn't move), what values have to change and how? - if the animation moves the blob by changinging the
left
and invokes yourprogress
callback, what values have to change and how?
Checking Location¶
Since getting x
, top
and all those values correct and in sync can
be difficult, I've provided the following method that will return a
string letting you know whether you got the basic invariants right:
location() {
let x = this.getX();
let y = this.getY();
let left = parseInt(this.getDOM().css('left'),10);
let top = parseInt(this.getDOM().css('top'),10);
let r = this.getRadius();
let xok = (left+r==x) ? "X OK" : "X WRONG";
let yok = (top+r==y) ? "Y OK" : "Y WRONG";
return `radius ${r} center (${x},${y}) w/ DOM elt (${left},${top}): ${xok}, ${yok}`;
}
You can include this with your other methods for Blob
objects.
I suggest you call this regularly when you are testing your blobs and enemies. You can test it easily from the JS console, like this:
> b1 = new Blob('red', 200);
Object { elt: {…}, radius: 200, color: "red", x: 0, y: 0 }
> b1.location();
"radius 200 center (0,0) w/ DOM elt (-200,-200): X OK, Y OK"
> b1.setX(150);
Object { elt: {…}, radius: 200, color: "red", x: 150, y: 0 }
> b1.location();
"radius 200 center (150,0) w/ DOM elt (50,-200): X OK, Y OK"
Intersection¶
I will give you the following code, which determines whether two circular
Blobs intersect. It uses the Pythagorean theorem to compute the distance
between their centers (d), and then compares that to the sum of the two
radiuses (r1+r2). Essentially, we want to return true
if and only if:
d < r1+r2
However, the Pythagorean theorem requires us to compute d as the square
root of the sum of two squares. The square root function is relatively
expensive (try computing one by hand, without a calculator). So, we use a
trick: we square both sides of that inequality, and we return true
if
and only if:
d^2 < (r1+r2)^2
Here's the code, using the new method syntax:
function isNum(val) {
if( typeof val === 'number' ) {
return val;
} else {
throw new Error('value is not a number');
}
}
class Blob {
//
intersects (other) {
// six uses of the 'isNum' function to make sure all values are defined
const dx = isNum(this.getX()) - isNum(other.getX());
const dy = isNum(this.getY()) - isNum(other.getY());
const r1 = isNum(this.getRadius());
const r2 = isNum(other.getRadius());
// finally, some real computation
const distance_squared = (dx * dx + dy * dy);
const rsum = r1+r2;
const isCloser = (distance_squared <= rsum*rsum);
return isCloser;
}
}
Note: I've defined a function isNum
that checks that its argument is
a number, since a common error is to have a broken coordinate or
radius, which makes the intersection code fail. This catches that
error. You're welcome to use isNum
in your own code.
Testing Blobs¶
The constructor for a Blob
should initialize the color and
radius. It can set the x and y of the center to zero; those will
change later. It will also create the DOM element that corresponds to
this blob and it will add it to the game, using the addToGame
method
to add the DOM element to the "body"
. (It would be better modularity
to keep addToGame
separate, but combining them makes testing and
debugging easier, and that's worth doing here.)
The following code, executed in the JS console, should give you a small, red quarter circle in the upper left corner of your page, and a small green blob below, just touching, and a small orange blog to its right, just touching.
b1 = new Blob("red", 50);
b2 = new Blob("green", 50);
b2.setY(100);
b3 = new Blob("orange", 50);
b3.setX(100);
Like this:
Here are some of the instance variables:
>> b1
Object { elt: {}, radius: 50, color: "red", x: 0, y: 0 }
>> b2
Object { elt: {}, radius: 50, color: "green", x: 0, y: 100 }
>> b3
Object { elt: {}, radius: 50, color: "orange", x: 100, y: 0 }
The details of the elt
are not show, but that's the DOM element (the
DIV.circle
) corresponding to this blob. If the DOM element doesn't
show up, check the element CSS.
Be sure to liberally use the location()
method that I provided to
check these methods.
You should check the working of blobs thoroughly before going on to implement Player and Enemy.
- Check that if you set the radius, the DOM element grows/shrinks correctly.
- Check that if you set the radius, the getter methods for both
getRadius
andgetDiameter
report the new value. That is, if you set the radius to 100,getDiameter
should return 200. - Check that if you set the X or Y location, the DOM element moves on the screen and the getter methods are correctly updated.
Many students get confused about how x
, y
, top
, left
, radius
and diameter
are all related. See object
location.
The testing.js
file has some useful testing functions, particularly
testDOM()
, testCalculations()
and a number of others. See
testing
Player Object¶
There will only be one instance of the Player object, but we'll define it
as a class anyhow. (Maybe there will be a multi-player version
someday...). Its constructor can have the same arguments as Blob
. The
player will inherit all the behavior of Blobs, plus the following methods:
move
which takes an x,y location and moves the DIV so that the center is in the new location.grow
which increases the radius bygrowRadius
pixels.shrink
which decreases the radius byshrinkRadius
pixelscollide
which is invoked when a collision happens: that is, when an enemy says "I got you". It takes the enemy blob as an argument and either grows or shrinks the player as appropriate.
The grow
and shrink
methods also compare the new size to some
limits. If the limit is exceeded, the game ends, with the player winning
or losing.
Here are some key values:
var winningRadius = window.innerHeight/4; // bigger than this wins
var losingRadius = 4; // smaller than this loses
var growRadius = 10; // grow by this many pixels
var shrinkRadius = 3; // shrink by this many pixels
Testing the Player object is much like testing the blobs, except there are a few new methods. Consider the following tests in the JS console. If you're not sure what they should do, just ask:
var thePlayer = new Player('blue', 15);
thePlayer.move(200,300);
thePlayer.grow(); // or .shrink()
To check the collision handling, just create an Enemy, like this:
bigBad = new Enemy();
bigBad.setRadius(50); // bigger than thePlayer
thePlayer.collide(bigBad); // big Enemy got me, I gotta shrink
Note that this code happens after the intersection checking, so it
doesn't matter where the bigBad
is; it only matters that it's bigger
than the Player blob.
We can also check eating small prey:
smallPrey = new Enemy();
smallPrey.setRadius(10); // smaller than thePlayer
thePlayer.collide(smallPrey); // ate this small Enemy; I will grow
The enemy should also disappear when it's eaten, because the player
will invoke the Enemy's remove
method.
The Player Object¶
Your Player
class will be named Player
, so the name of the
global variable must be different. Remember that classes and
instances are different things, so they need to be stored in different
variables.
You'll notice above that I consistently named my global variable
thePlayer
like this:
var thePlayer = new Player('blue', 15);
I recommend you do the same. Your Player.js
file will define the
Player
class and declare the global variable, creating an instance
of the class.
While this isn't ideal modularity, it's not bad and makes testing a little easier — a worthwhile tradeoff.
Enemies¶
Enemies have some interesting and complex behavior, since they are autonomous.
The constructor of an enemy takes no arguments and does the following:
- gives it a random color and a random radius
An earlier version of these directions also said that the constructor should specify the Enemy's direction and location, but it's easier to put that off and make the user do that.
For the random color and direction, use the functions in random.js
,
which I supplied, and you should load without editing the file, but
you ought to read the file.
The enemy blobs always move in a straight line directly across the window,
so if they come in from the left, they go in a horizontal line and exit on
the right. Their y
(top
) coordinate never changes. (You can imagine
many fun improvements and variations on this game.) Choose their
direction or starting side randomly among the four possibilities.
Enemy blobs can collide with the player. (As noted above, the player
object is stored in a global variable defined in Player.js
.)
Oddly and asymmetrically, it's the enemies who check for collisions, not the player. In other words, when an enemy moves, it checks to see if it has collided with the player, but when the player moves, it doesn't check to see if it has collided with any enemies. This rule, odd as it is, has the advantage that an enemy only has to check one other blob for collisions (namely the player), while the player would have to check against every enemy. This rule means that the player code doesn't have to worry about all the enemies (the human player does).
In the finished game, enemies start and end off-screen, but that can make debugging hard, so I suggest that, at first, you have them start and end at the edges of the screen. Once their motion is all debugged, you can have them start and end off-screen. (That's the difference between the regular and "debug" mode in my solutions, above.)
Furthermore, enemies can only collide with the player once. This means, for example, that if the enemy moves over the player in ten steps, that doesn't count as ten collisions. To implement this rule, an enemy will have to keep track of whether it has collided with the player, and if it has already collided, skip any further collision checking.
Whenever an enemy moves, it does the following:
- updates the values of X and Y from the DOM object. As we'll see later,
jQuery animations modify the CSS properties, and our code will have to
extract the values of
top
andleft
in order to determine the center of the blob, so that theintersects
algorithm above can be used. - it then uses the
intersects
method on itself andthePlayer
. If that indicates that an collision has occurred, it - invokes
thePlayer
'scollide
method, described above.
Setting initial coordinates is a bit tricky, but it breaks down into two or four straightforward cases:
- If the blob is entering from the top (or bottom) moving vertically,
choose its X coordinate randomly between the left and right edge of
the window (0 and
window.innerWidth
). The Y coordinate is chosen so that the blob is just at the edge of the screen. So, zero for the top edge andwindow.innerHeight
if it's entering from the bottom edge. - if the blob is entering from the left (or right) moving
horizontally, choose its Y coordinate randomly between the top and
bottom edge of the window (0 and
window.innerHeight
). The X coordinate is chosen so that the blob is at the edge of the screen, so zero for the left edge andwindow.innerWith
for the right edge.
The enemy has a remove
method that stops the animation and removes
the DOM element from the document. This method is used by the player
when it eats a smaller blob or when the animation is done. (The player
testing code above shows how to test this: just create big and small
enemies instead of just blobs.)
The enemy has a start
method that starts its animation going. So, in a
moment, we'll turn to animation to learn more. First, though, here are
some values you might want:
var minRadius = 4; // random size >= this
var maxRadius = window.innerWidth/8; // random size <= this
var enemyDuration = 5000; // time to cross the page
Here's a complete list of the methods of the methods I implemented for my Enemies objects:
collide
which is invoked when a collision happens and it records that the enemy has collided with the player and informs the player of the fact. That is, it invokes the player'scollide
method (see above). Remember that the player object is stored in a global variable that is declared and initialized inPlayer.js
.updateLocation
which updates the X and Y location of the center from the top/left CSS values. This is helpful when the animation moves the enemy and you need to update the location before testing for intersection with the player.maybeCollide
which checks for a collision. It's invoked during the animation of the movement of the enemy. The method first updates its location. Next it checks to see if this enemy has collided with the Player in the past and if so, skips any further processing. If it hasn't collided in the past, it checks to see if there is an intersection (using theintersects
method above) and if so, invokes thecollide
method that we just discussed.setSide(side)
which takes one argument, a string indicating which side of the screen the enemy is entering from. The argument is one of "top", "right", "bottom", and "left". The method sets the initial X,Y coordinates of the enemy, based on the side it enters from. It also records the side, as that makes thestart
method easier.start
which starts the jQuery animation of this enemy moving across the board to its final X/Y value.remove
which stops the animation and removes this enemy from the board
Many people are confused by the difference between maybeCollide
and
collide
. See collision methods below.
Testing the enemies is a little tricky because the enemies are always moving. But we can test the setup. We can also test what happens during a single step ("frame") of the animation. Setup is like this:
e1 = new Enemy();
e1.setSide('top');
The enemy should have a random starting location on the top edge of the board. You can test the other three sides similarly.
Testing updateLocation¶
The animation will modify the CSS top
or left
values to move the
enemy. Let's similate one step of that. In the code below, I've gotten
the DOM element and modified its CSS (this is what the animation will
do).
e1 = new Enemy();
Object { elt: {…}, radius: 17, color: "Bisque", x: 0, y: 0, collided: false }
e1.setRadius(100);
Object { elt: {…}, radius: 100, color: "Bisque", x: 0, y: 0, collided: false }
e1.location();
"radius 100 center (0,0) w/ DOM elt (-100,-100): X OK, Y OK"
d1 = e1.getDOM()
Object { 0: div.circle
, length: 1 }
d1.css('top', 100).css('left', 200);
Object { 0: div.circle, length: 1 }
e1.location();
"radius 100 center (0,0) w/ DOM elt (200,100): X WRONG, Y WRONG"
e1.updateLocation();
undefined
e1.location();
"radius 100 center (300,200) w/ DOM elt (200,100): X OK, Y OK"
This code places the enemy at the upper left corner of the page,
showing just a quarter of it. Moving the DOM element means the x,y
values are wrong, as the location
method shows. After the
updateLocation
method runs, the X and Y location of the enemy should
have the correct values, which the location
method confirms.
Remember that the jQuery .css
method can be used to either set the
css value: $(elt).css('top',newVal)
or it can be used to get the
current value: $(elt).css('top')
. The latter, however, returns the
value as a string with 'px' at the end, like '50px'
. To parse that
string to extract the value as a number that you can do arithmetic
with, you can use the builtin JavaScript
parseInt
function:
parseInt('50px', 10) // returns 50 as a number
Testing Intersection¶
Next, to test intersection, let's create a Player that doesn't intersect, test that, and then move the Player so that it does.
e1 = new Enemy();
e1.setRadius(100);
e1.getDOM().css('top',0).css('left',0)
e1.updateLocation();
thePlayer = new Player('blue',15);
thePlayer.move(300,300);
e1.maybeCollide(); // doesn't collide
thePlayer.move(200,100); // on the right edge of the enemy, definite collision
e1.maybeCollide(); // does collide, player shrinks
e1.maybeCollide(); // second collision doesn't count
Once all that works, you're ready to start putting the animation together.
jQuery Animation¶
Animation in jQuery is very cool and powerful. You can read a ton about the animate method on the jQuery site.
I used a version of the jQuery animate
method that looks like this:
$(elt).animate({left: 500}, options);
That method starts moving the element by adjusting its left
CSS
property until it is 500px, regardless of its starting
value. Obviously, you won't use exactly that code, but that example
should get you going.
The options
argument in a JS object literal that can have many
properties. I used three:
duration
a time in milliseconds for the animation to take. Smaller numbers mean the enemies move faster and the game is more challenging.progress
a function that jQuery invokes at every step of the animation. This is a good time to update the location and check for collisions. If you want to use a method of an object, remember that you'll need a value forthis
. Arrow functions might be useful here.complete
a function that jQuery invokes when the animation is done. Again, if this is a method, you might find an arrow function to be useful.
You can stop an ongoing animation by using the jQuery
.stop()
method:
$(sel).stop();
Animation and our Model¶
The jQuery animation happens separately from our JavaScript code. The
jQuery animation incrementally updates the top
or left
CSS values
of the DOM object (the div.circle
that we discussed above). By
default, the jQuery animation does not update anything in our Blob
object: no instance variables are modified.
Let's take a concrete example to explain what is going on. Suppose we have
a blob with a radius of 10. It starts out nestled snugly in the upper left
corner of the screen, so the CSS looks like top:0px;left:0px
and the
Blob
object has instance variables recording that the center of the blob
is at (10,10).
We then start an animation where the blob is moving diagonally across the
screen. After a while, the CSS values will be top:500px;left:500px
but
the Blob
object's instance variables still have the center at
(10,10). That's because the animation only updates the CSS values we asked
it to, nothing else.
If we want it to update something else, namely the blob's center, we can
use the progress
callback. (This is information flowing from the
animation back to our JS code.) Here's an example of the idea, from the
testProgress
testing function in testing.js
:
function testProgress() {
$(".circle").remove(); // remove any prior blobs
testBlob = new Enemy();
testBlob.setX(100);
testBlob.setY(100);
$(testBlob.getDOM())
.animate({ left: 500 },
{ duration: 3000,
progress: function () {
testBlob.updateLocation();
console.log("x is now ",testBlob.getX());
}});
}
Of course, the code above is not sufficiently modular or generic. You
should define a method that does the right thing here. Again, you
might find arrow functions useful, since you need to a function with
the correct value of this
for the progress
callback.
Testing¶
In my experience, the most common student errors came in
- getting the blob model correct: the correct center x,y and radius
- updating the blob model from the animation, as described in the previous section
- computing the intersection of two blobs. Once the model is correct, this is usually a straightforward use of the code I supplied.
To make this easier, I am supplying some testing code in
https://cs.wellesley.edu/~cs204/assignments/jelly/testing.js
The reference solution loads that file. You should try the testing functions in the reference solution and compare them with your own.
The functions testDOM
and testCalculations
will help you test your
Blob implementation. These work without any obvious stuff on the
screen, so you'll use them in the JS console. The next few functions
display blobs on the page, so there have a visual component, and there
are screenshots below.
The testIntersection
function puts two blobs on the screen, just
touching. You can move the green one relative to the red one, so that
sometimes they intersect and sometimes they don't. Read the examples.
The testProgress
function allows you to test your own progress
callback, as described above.
Here are screen shots of the various testing functions working with my
solution. The testBlobDisplay*
functions create and display a green
blob in a red box, so you can test the basic display and sizing
constructor and methods. Note that there's no visual output from
testBlobDisplay4()
so I've omitted that, but you should stil run it
for the console.log statements. There are also functions to test the
blob intersection, which are the last two pictures. In the first one,
the blobs just overlap by a pixel or so. In the second, they have been
moved just a little and don't intersect. Your code should work the
same. Again, try these in the reference solution to see how they
should work, then try them in your own solution.
Mouse Movement¶
One of the exciting things that players do is have their movement
determined by the user's mouse. This is surprisingly easy. We just have
to set up an event handler for an event called mousemove
. That event
happens, you guessed it, every time the mouse moves. As with all events,
the handler is invoked with an event object that captures important
information about the event. Here, the relevant information is the
location of the mouse, which are in properties clientX
and clientY
.
So, code like:
$(document).on('mousemove', function (evt) { ...evt.clientX,evt.clientY... });
Your event handler should update the blob representing the player and
that should indirectly update the blue div.circle
on the screen. I
suggest the move()
method you defined.
Random Stuff¶
You'll need a bunch of code for generating random values and random
colors. I've provided some useful functions for you in
https://cs.wellesley.edu/~cs204/assignments/jelly/random.js
Again, that's loaded by the reference solution. You should do the same and use the functions in it.
Automatic stuff¶
To keep the enemies coming as long as the game is played, we need to generate a new one every so often. To do that, I used setInterval, which we also learned (will learn) in the context of automatic slide shows:
var intervalID = setInterval(function () { ... }, delay_in_milliseconds);
It's really just another callback: we write a function, hand it to the browser and the browser invokes it at the appropriate time(s). In this case, every N milliseconds, where we specify the N.
Recall that the return value from setInterval
, which I stored in
intervalID
above, can be used to stop the interval, using
clearInterval. You'll
find that useful in stopping the game:
clearInterval(intervalID);
I suggest you define a function called launchEnemy
that does all the
work to launch a single enemy. Test this in the console to make sure
it works. Later, you can use setInterval
and launchEnemy
to
automatically launch one every second.
Starting the Game¶
The home page is fairly simple: there is some explanatory text and one
button. The button starts the game by invoking the startGame()
function.
The startGame
function does the following:
- Removes all the other info stuff from the document, clearing the slate
- Uses
setInterval()
andlaunchEnemy()
to start generating a new enemy every 1 second. - create a new player object, setting the global variable
thePlayer
, and putting the player in the center ofthe board. - set up the mouse movement event handler for the player.
I made the player be blue (and 'blue' is not a possible random color) and initially it is 15 pixels in radius.
Stopping the Game¶
You should define a function, stopGame(result)
to stop the
game. It'll be used by the player to stop the game when it is won or
lost. That function should:
- stop the generation of new enemies
- stop the animation of all current enemies
- announce the result, which is an argument
The best place to invoke the stopGame(result)
function is in the
Player code that grows or shrinks the player, because that is the time
that the game is either won or lost.
After the game ends, the human playing it can reload the page to play again. There's no "replay" button.
Hints¶
There are a number of hints I routinely give out. This covers most of them. Feel free to ask if you're confused about anything.
Element CSS¶
Many people have managed to create a blob and add it to the screen, such that they can see it in the Chrome Inspector, but the blobs are invisible. A complete active blob will have all of the following CSS properties:
position:absolute;
border-radius: 50%;
left: ...;
top: ...;
width: ...;
height: ...;
background-color: ...;
Remember that CSS's block model wants to know the width and height of the
rectangular region occupied by the blob and the absolute positioning needs
to know the left and top. The intersection code needs to know the center
coordinates and radius. There's an obvious relationship between the two,
but keeping these in sync is tricky. Here's a screenshot that may help,
where I've put the mouse over a blob I added to the screen using the
testIntersect
function:
Putting Elements Off-stage¶
Putting overflow:hidden
on the body
will keep the body from becoming
wider or taller when there is an enemy coming in from the right or the
bottom.
Collision Methods¶
The naming of the three collision methods can be confusing. Here's one way to think about them:
Enemy.maybeCollide
: called constantly during animations. checks for collisions with the player. Think of it asdidIgetThePlayer?
Enemy.collide
: called when an actual collision happens. Think of it asIgotThem!
Player.collide
: called fromEnemy.collide
when an actual collision happens. Think of it asyouGotMe
Modularity¶
This assignment is hard enough, so don't worry about packaging things up in IIFE modules.
Getting Started¶
You're welcome to use some of my files, just not the obfuscated .js
files:
cd ~/public_html/cs204-assignments
mkdir jelly
cp ~cs204/pub/assignments/jelly/solution/jBlobs.html jelly
cp ~cs204/pub/assignments/jelly/solution/jBlobs.css jelly
How to turn this in¶
Do all your work in a new folder called jelly
; turn in a Gradescope item.
Tutors/Graders¶
You can use the password to view the solution
Here's the version with debugging turned on: solution w/ debugging
Time and Work
Finally, when you have completed the assignment, make sure you fill out the Time and Work report. That report is required.