I posted this animation to Twitter a few weeks ago, and I thought I’d walk through the origin story of how it came to be and the steps I took to create it.
The rope documentation says
A Rope is a Sprite that has a repeating texture. The texture will automatically wrap on the edges as it moves.
As the texture moves? The rope? I’ve tried moving the rope around and adjusting the crop and frame of the texture, but nothing works. I’m using ropes for waves in my game (http://orangesea.oddkraken.com/), and I want to scroll the texture by while keeping the waves moving up and down, so it looks like the player is moving to the right.
Is there a better way to go about this? Thanks.
This particular use of
Phaser.Rope is not possible at this time. A Rope’s texture ends up being “glued” to the rope object, and cannot be transformed left/right down the rope’s geometry. Each segment of the rope is “fused” to the texture at it’s x-position at creation-time, essentially. See the video below and note that each green square is a segment of the rope, and inside that segment, the texture is mapped to that square, and cannot be shifted left or right (or up or down).
But, that does not mean we cannot accomplish something similar in Phaser, so I tried my hand at it.
What I needed
To build this example, I would need some assets. I went to work inside of Pyxel Edit and built a little boat, and a spritesheet of some waves. I tend to make all my pixel assets as 32x32 images, then scale them up on export, or inside of Phaser (note: if you scale up in Phaser, you’ll want to set
yourSprite.smoothed=false; or else it’ll look blurry.)
Great! Assets done and looking pretty good.
Waves need to move like waves
Next, I needed to come up with a way to place these waves next to each other, and allow them to move independently in a “wave-like” fashion. Rolling and bobbing up and down.
Math.sin function in my
update function that gets called once per frame:
Math.sin an x-value, outputs a y-value at that x. As x increases, the y value oscillates up and down. If you were to plot these y values for every x value from 0 to 10, let’s say, you’d see a wave-like pattern emerge. This is obviously perfect for what we are trying to accomplish here!
In our case, we give
Math.sin and x-value that is composed of two things: the current wave’s number,
i and a counter (
this.count) that increments by 0.1 every time a frame is drawn to the screen (30-60 times per second!). After getting the y-value out of
Math.sin(x), we multiply that value by 5 to increase the height (amplitude) of the wave. If we did not multiple, we’d have very tiny wave motions and I wanted the waves to appear to be large and rolling.
Boat and waves should collide
Now that the waves are moving, we need a way to collide them with a boat. Phaser has built-in physics that can handle this by creating a “physics body” for each element that needs to interact with the physics engine. In our case, since the boat, and each wave are going to collide, that means all of those elements will need a physics body attached to it. By default, the physics body is square and is most often referred to as a “hit box”. But, for our purposes, I thought I’d try to use a circle, and you’ll see why in a little bit.
I’ve highlighted the physics bodies (green) in Phaser to show the boat and waves colliding. Once we add some velocity to the boat, it will collide with each circle it meets, giving its path an irregular shape, almost as if it were riding up and down the slope of a wave. That’s why using a circle physics body on the boat and all of the waves works better in this instance.
In order to make this more interesting, the boat needs to move and when the boat moves, the waves need to “shift” to the left adding new waves off the right side of the screen, and disappearing waves off the left side as they go out of view. This is how we’ll create an “infinite” sea for the boat to explore. As its velocity moves it to the right, we’ll constantly swap in new wave tiles so it always has something to sail on.
As you can see, the boat eventually falls off the last wave, and off the screen. We need to implement the “infinite” part of this example, and continuously swap the left-most wave sprite to the right side of the screen, so the boat can never “fall off” the screen and to certain death below.
How to I swap tiles?
On initialization of my scene, after creating each wave sprite, I set a few variables to keep track of where the first and last wave are in the group of waves. The idea is that these indexes will change as the sprite moves from right-to-left across the screen.
Note: At first I attempted to treat the array as a stack, and “pop off” the wave sprite that was “off the screen” on the left, and then insert it back on the top of the stack, placing it on the far right of the screen. This caused redraw problems, I think, because I would get a 20-50ms stutter every time I had to pop and push. That is how I came up with this index-tracking mechanism.
Once per frame of animation, I take a look at the first and last wave in my group of wave sprites. If the first wave has fallen off the screen (it’s left-most x-position is less than the camera’s x-position), move the sprite to the far right by setting its new x-position just to the right of the current last wave sprite. Then, set the
lastWaveIndex to the
firstWaveIndex, since it was just pushed to the far right, and increment the
firstWaveIndex by 1 to make the now left-most wave sprite the “first” sprite. I also correct for if we increment the index and it’s greater than the number of waves we’re currently rendering. In that case, I set the
firstWaveIndex to 0 to “wrap-around” the index.
I’ve numbered each wave with its index, so you can see them repeat themselves after the 5th wave. There are only six waves total, numbered 0, through 5.
How did the boat move “endlessly”?
The boat has a velocity on its x-axis that constantly propels it forward through the world. This particular game world is only 10,000 pixels wide, but again, I’m only using enough wave segments to fill the width of the screen. The camera is also fixed to the boat, so as it moves, the boat stays centered.
An alternate way to “move” the boat would be to not give it a velocity at all, and experiment with simply moving the waves to the left, and the boat would naturally collide with the wave bodies and slide over them. I’d have to make sure the boat could not slide backwards and only move forward, and I’m not sure how easily that would be done.
Additionally, I could keep the X-velocity on the boat, and then as the player got to a world X coordinate (say, 9,000 of 10,000 pixels), I could reset the player’s X position back to world-position 0, since the waves are fixed to the camera, and then the boat would be free to travel for another 9,000 pixels and never see the “end” of the world at 10,000 pixels. At this rate the world wouldn’t even have to be 10,000 pixels, but instead, just wide enough to fit the entire user’s viewport, plus some padding to the right, and then as the user got to the edge of this world length, reset them back to 0.