ID:99026
 

To go right to the hub entry, click here.

Update: I also created a similar demo that shows how to use these techniques with isometric maps: link

Pixel movement is not built into BYOND. That's not because it's incredibly difficult, but because there are many options for how to implement it. Pixel movement allows for more complex movement situations, more complex than BYOND's built-in tile-based movement system. There are many ways you can resolve these situations, so the implementation details are left to you.

BYOND's default collision model assumes that an atom takes up an entire tile and that you move one whole tile at a time. Because you are moving from one tile to another, your movement is all or nothing: it either fails or succeeds. If you try to move to a dense turf, you cannot. Pixel movement lets you move a fraction of a tile at a time. Because you can be standing at an arbitrary position within a tile and move a fraction of a tile, this creates many more possible movement scenarios:

On the left we see tile based movement. When you want to move up, there are two possibilities: the tile is dense or not dense. We can easily determine which move succeeds and which one fails. On the right we see pixel movement. What happens in these cases? Does the move fail completely or partially succeed? How do we figure this out?

First of all, we don't want the move to completely fail. Consider this situation:

The blue box is 6 pixels from the wall and moves 9 pixels to the right. If the movement failed completely the blue box would not move at all. What we want to happen is that the blue box moves 6 pixels to the right. Because the blue box is moving in one direction this is fairly easy to see why that should happen. Let's consider a more complicated example:

In this example the blue box is moving down and right. It ends up inside the wall, so we need to figure out how it hit the wall. Did it the top of the wall first or the side?

If we could look at the movement in "slow motion" we'd see that it just barely hits the top first:

But how do we make the program figure out which side it hit first?

We know how far inside the obstacle the box ended up and we also know how much the blue box was trying to move. If we compute x/dx this will tell us how far through the blue box's move it hit the side of the obstacle. Similarly, y/dy will tell us how far through the move it hit the top of the obstacle.

Suppose that x/dx is 0.5 and y/dy is 0.4. This means that 40% of the way through the move, the box hit the top of the obstacle. If it didn't hit the top first, it wouldn't have been until 50% of the way through the move that it hit the side. Because we know it hit the top first, we know how to adjust the box's position.

Fancy Collision Detection

In this example I treated all objects as rectangles. It is easy to see when and how rectangles intersect. You might want to use more complicated shapes for checking collisions (circles, rectangles that can rotate, etc.) but this can really complicate things. As long as your objects are rather small, I would suggest using a set of rectangles to approximate the object. A move is successful if all parts of the object move successfully.

One More Thing...

If you have an object moving really fast it can skip right over an obstacle if you're not careful. If you only check the final position of the moving object (and you don't consider what it had to move through to get there) then you might pass through an obstacle.

To avoid this you can keep the movement speeds small as compared to the size of the obstacle. If bullets can move 50 pixels per tick, they could skip right through a wall that's only 32 pixels wide.

To deal with fast-moving objects, break the move into separate parts, where each part moves the object a manageable distance. In the example of a bullet that moves 50 pixels per tick, treat its movement as two separate 25 pixel moves that occur on the same tick.

Sidescrollers

A basic sidescroller is about 80% pixel movement, 20% physics. Once you have a decent pixel-movement system the rest is easy to implement.

Demo

You can download a demo of these techniques here.

_include.dm contains code used by both demos.

_pixel-movement.dm contains code to handle pixel movement. The main proc there is mob.pixel_move, which moves a mob by a specified number of pixels taking obstacles into account.

demo-1.dm and demo-2.dm are separate demos of how to use the pixel movement system. The first demo is very simple. The second demo is a sidescroller. It is intended that you only include one of the demo files when compiling and running the project.

Isometric Pixel Movement

The isometric pixel movement demo can be found here: http://www.byond.com/developer/Forum_account/IsometricPixelMovement

That demo uses the same techniques described in this post. To handle height and jumping we need to add additional calculations to handle the additional dimension.

In the 2D top-down demo you could not bump into the white tiles, they were not dense. In this isometric demo we abandon the idea of "dense tiles". Every tile is dense because it has a floor, the question is: how high is the floor? We introduce the pdepth variable to determine how tall an obstacle is and we update the collision checks to take this into account.

Each tile has a floor, but no ceiling. You can't jump and hit your head on anything so we don't need to worry about collisions in the z-direction when your dpz is positive (when you're moving upwards).

All those pictures are broken.
They're not broken, they're just really boring and useless!

They were showing up for me. Do they work now?
Yep, they're fine now.
Excellent! but the only thing i found annoying was the non-stop moving once i let go of my key. There may be something in there in your explanation, but i'm too lazy to read at the moment.
Rickoshay wrote:
Excellent! but the only thing i found annoying was the non-stop moving once i let go of my key. There may be something in there in your explanation, but i'm too lazy to read at the moment.

I don't think I gave an explanation, but here's one:

To keep the demo simple it uses BYOND's internal key repeat. If you hold right to run then press jump, the right key stops repeating. Every time you'd jump you'd stop moving horizontally.

There is a better way to handle this using a custom interface file and macros, but it would make the demo more complicated and wouldn't relate to the pixel movement so much. I might add this later.
An easy fix to that would be to make the jump verb a while() loop.
Fugsnarf wrote:
An easy fix to that would be to make the jump verb a while() loop.

I'm not sure how that would fix it.
Awesome demo. Thanks for sharing.

ts
Forum_account wrote:
I'm not sure how that would fix it.

Well, it would allow you to take off the auto-movement that turns on when you hold it awhile. The reason the jump and move keys cancel either other out is stated in the release notes when the new repeat feature was made: "If a new repeating macro key is pressed while the first is held down, the new key will take precedence."

This might mean that jump is repeating. If it isn't repeating, it shouldn't be canceling out the current repeating movement that's going on. When I changed the movement in my Megaman game's pixel movement to use the repeat, I kept my jump as a while() loop (since you go higher as you hold it down and stop when you let go) and it doesn't cancel each other out.
Fugsnarf wrote:
Forum_account wrote:
I'm not sure how that would fix it.

Well, it would allow you to take off the auto-movement that turns on when you hold it awhile. The reason the jump and move keys cancel either other out is stated in the release notes when the new repeat feature was made: "If a new repeating macro key is pressed while the first is held down, the new key will take precedence."

This might mean that jump is repeating. If it isn't repeating, it shouldn't be canceling out the current repeating movement that's going on. When I changed the movement in my Megaman game's pixel movement to use the repeat, I kept my jump as a while() loop (since you go higher as you hold it down and stop when you let go) and it doesn't cancel each other out.

I believe the jump key is repeating. Is there an easy way to tell it to not repeat?

The improved built-in repeat rate is nice, but ideally you'd want finer control over things anyway. You should only jump when you press up. Holding it down makes the key repeat so you jump immediately as you hit the ground.
Make an interface file and you can edit all of that in "macros".
Fugsnarf wrote:
Make an interface file and you can edit all of that in "macros".

If I'm going to go through that much trouble I might as well handle all keyup/down events. Here is an example of better controls based on the state (up/down) of the arrow keys.
How can I let my mob finish it's movement? If I move, it's stuck on its first frame so it slides on the ground and twitches with the other frames sometimes.
DisturbedSixx wrote:
How can I let my mob finish it's movement? If I move, it's stuck on its first frame so it slides on the ground and twitches with the other frames sometimes.

You're talking about a movement animation, right?

You can set the mob's icon_state based on a few things: is the mob moving? in which directions are they moving? are they on the ground? That way you can have separate animations for walking, standing, or jumping.
Forum_account wrote:
If I'm going to go through that much trouble I might as well handle all keyup/down events. Here is an example of better controls based on the state (up/down) of the arrow keys.

I don't really see it that way as trouble to go through. I just see it as another necessary step in the development process. Do you find it to be that much of a hassle to deal with the interface file?
Forum_account wrote:
DisturbedSixx wrote:
How can I let my mob finish it's movement? If I move, it's stuck on its first frame so it slides on the ground and twitches with the other frames sometimes.

You're talking about a movement animation, right?

You can set the mob's icon_state based on a few things: is the mob moving? in which directions are they moving? are they on the ground? That way you can have separate animations for walking, standing, or jumping.

Well this is isn't for a side-scroller. I just need to let me movement animations for the four directions finish.
Fugsnarf wrote:
I don't really see it that way as trouble to go through. I just see it as another necessary step in the development process. Do you find it to be that much of a hassle to deal with the interface file?

Yes.

The demo is meant to show ways that you can easily use pixel movement. I provide a basic example which people can take and build off of. If my demos are very complex (multiple dm files, interface files, etc.) people will think it's difficult to use pixel movement in their game. I'm trying to show how simple it can be, so keeping interface files out of this is a good thing.
DisturbedSixx wrote:
Well this is isn't for a side-scroller. I just need to let me movement animations for the four directions finish.

Because you can move a fraction of a tile at a time, the player might stop moving before the entire movement animation completes. This also means that moving is a sequence of really small steps, so you can't just flick the animation each time client.North/South/East/West is called.

You need some way to know whether or not the player is moving. When they are moving, switch them to an animated icon_state. When they stop (ex: when a few ticks have passed without them hitting an arrow key), switch them back.
Okay I've made two icon_states. One when the character is moving, which has two frames for the two steps, and one that is one frame of the character standing. Now, I've named them both the same thing but made the two framed icon_state a movable state so that when the character is moving, that is the animation shown and when he is not, the other. Still, the movement is twitchy. Is it because of my settings somewhere? The tick_lag=0.25, the character takes 4 pixel steps at a time (different from the default 7 you have in your demo), and the delay of the two frames is 1. Am I doing something wrong?
Page: 1 2 3