Extra Balls
Now back to coding our power ups! We can just add little code snippets to each of them. For ExtraBall I think you can already handle it, so why don’t you try it first
Just another instance_create_layer()
call 😉
// oExtraBall Collision w/ oBall Event
instance_create_layer(
oBall.xstart,
oBall.ystart,
layer,
oBall
);
You could have done this using a collision event in oBall
instead, I just figured I’d do it in the power up, since oBall
is starting to get cluttered (also this code also seems more closely related to the power up)
Here’s what it looks like when testing (hint, update the spawn logic to only spawn oExtraBall
to make testing easier )
Grow Ball
This ones a little more involved. I can drive for this one
Reference by object
Here’s the first way we might implement this
// oGrowBall Collision w/ oBall Event
oBall.image_xscale += 1;
oBall.image_yscale += 1;
image_xscale
and image_yscale
are new. They control the scaling of instances along the horizontal and vertical directions
Initially I was setting the scale to 2, but I think it’s cooler to add 1. That way the ball(s) can keep getting bigger and bigger 😈
If you play w/ this for a while, you might notice that the growth behaviour might not work as you expect. Every power up seems to update ALL balls
Remember oBall
is an object not an instance, so changing oBall.image_xscale
changes the scale across all balls. Not only that but retrieving oBall.image_xscale
will still only give you one value. So oBall.image_xscale = oBall.image_xscale + 1;
(the equivalent of oBall.image_xscale += 1;
) will retrieve oBall.image_xscale
from a single oBall
instance, add one and then set that across all oBall
instances. So in the example, it happened to use the oBall.image_xscale
from the biggest ball, so everything else skipped several ranks
It’s not totally clear how these power ups should interact w/ each other, so take a second to think about which option sounds more logical to you
Apply growth to colliding ball (using other)
Let’s say you only want to apply the growth to one ball. In this case it makes the most sense, for the colliding ball to always receive it. Here’s how you’d do that
// oGrowBall Collision w/ oBall Event
other.image_xscale += 1;
other.image_yscale += 1;
...
other
is a special keyword that can be used in collision events as well as a few other places. This let’s us refer to the specific other
instance that was involved with the collision. Note other instance not other object, this means it really is that specific ball, and not the oBall
object in general
with keyword
Cool, we’ll make all the balls grow in a second. But first I want to talk a bit about with
. with
is a keyword you can use to execute code in a different instance or object. For example, here’s how I’d rewrite the above code using with
// oGrowBall Collision w/ oBall Event
with(other){
image_xscale += 1;
image_yscale += 1;
}
...
For this particular case, using with(other)
versus other.
is a matter of preference. For longer snippets I tend to use with
, but for shorter snippets (1 or 2 lines), I’d probably use .
Apply growth to all balls (using with)
Here’s how we can use with to perform the scaling on all balls
// oGrowBall Collision w/ oBall Event
with(oBall){
image_xscale += 1;
image_yscale += 1;
}
...
It looks almost identical, but there is a critical difference. When you pass a object (like oBall
) into a with
statement instead of a instance (like other
), it actually acts like a loop. The above code will repeat that snippet of code across across all instances of oBall
, thus making all of them grow by one
Now that I’ve given you a few options, I’ll leave it up to you which approach to move foward with. It really only depends on which one you like best for your specific pong game
Refactor paddle speed
I’d like to start working on the power ups to make the paddles faster, but right now that’s kind of hard since we’re hard coding the paddle speeds in the paddle code (I’ll use the terms hardcoding and magic numbers interchangably, they’re pretty much the same for our purposes).
TODO insert refactoring definition TODO mention that hard coding is a broader concept that magic numbers
It would be much better if we had a variable that our power up could adjust. Can you add a variable called max_speed to each of the paddles, and then refer to that when you move the paddle?
Luckily our paddles don’t have too many events, but there’s still a decent amount of refactoring to be done
I also opted to change our math logic to the superiour +=
😉
// oPaddle Create
max_speed = 4;
// oPaddle Key Down - Up
y -= max_speed;
// oPaddle Key Down - Down
y += max_speed;
// oEnemyPaddle Create
max_speed = 4;
// oPaddle Key Down - W
if(is_ai == false){
y -= max_speed;
}
// oPaddle Key Down - S
if(is_ai == false){
y += max_speed;
}
Why not just say speed instead of max_speed? Well, max_speed is more correct, because the paddles can have 0 speed if they’re not moving. But I also specifically avoided speed because it’s a builtin variable. We saw with the ball logic, that setting speed will cause the instance to automatically move in accordance with the direction logic. The paddles don’t use to the speed/direction approach and to ensure it stays that way I need to avoid setting speed/direction
Speed Up Paddle
Now that the refactoring is done, adding the new feature will be pretty easy (this is usually how it goes)
//oSpeedUpPaddle Collision w/ oBall
if(other.hspeed < 0){ // if the ball if moving left
oPaddle.max_speed += 2;
}else{
oEnemyPaddle.max_speed += 2;
}
For speeding up the paddle, I want the paddle that last hit the ball to be the one who gets the speed boost, presumably that’s the paddle that aimed the ball to the power up. To figure out which paddle that was, I check whether the ball is going left or right
Aside from that the code should be pretty simple. Most of the effort really was in refactoring our code to use a max_speed variable
Spawn Range
After playing with power ups for a bit, you may have noticed that the power ups sometimes spawn in awkward places. How about you try fixing that? (Hint: try looking at random_range)
//oPaddle Alarm 0 Event (updated)
instance_create_layer(
random_range(200, room_width-200),
random_range(200, room_height-200),
layer,
choose(oGrowBall, oExtraBall, oPaddleSpeed),
);
alarm[0] = 10 * room_speed;
Now I’ve got a 200 pixel margin along the edges where power ups won’t spawn
Note that in the spirit of reducing magic numbers, I’m still using room_width/room_height to ensure the margin adjusts to different room sizes even though it requires some extra math
And there you go, power ups! This made our little pong game a lot more interesting 😎