ID:105743
 
I started with a simple pixel movement demo a few months ago and it grew in all directions. As a result many hub entries were created to show the different demos: top-down pixel movement, isometric pixel movement, isometric platformer, sidescrolling platformer. Recently I started turning these demos into libraries, resulting in even more hub entries.

The end result is two libraries. Each pixel movement demo I had created has been removed from the hub and is represented as a demo contained in a library. The two libraries are:

Forum_account.PixelMovement
This library provides a pixel movement system for top-down and isometric maps. It supports elevation and jumping so you can use it to create action/platform type games or just use its basic pixel movement to create smoother movement in action games. This library takes the place of Forum_account.Isometric.

Forum_account.Sidescroller
This library provides a pixel movement system for sidescrolling action/platform games. It has no support for isometric maps.

The libraries are very similar. They both have global loops that call the movement proc for each mob every tick. They have many other vars/procs in common, such as: px, py, can_bump, move, movement, can_jump, pixel_move, set_pos, set_camera. There are some crucial differences so I don't expect to ever combine the two libraries, but they're similar enough that if you're familiar with one you should be able to pick up the other easily.

I was getting fed up with having a messy hub, so hopefully this hub cleanup helps you as much as it helped me.
Do you use vectors?
Yes.
How do you jump off of ladders?
You are God
By default there's no key for it.
The next step would be to add replacements for some of the commonly used default procedures. It would be much easier to turn the tech demos into real games if things like get_step(), range(), get_dist() and so on worked on a per-pixel basis.
SuperAntx wrote:
The next step would be to add replacements for some of the commonly used default procedures. It would be much easier to turn the tech demos into real games if things like get_step(), range(), get_dist() and so on worked on a per-pixel basis.

I'm not convinced that per-pixel equivalents of those procs would be very helpful. For some of them (get_step in particular) it's not clear what they'd do. I could create per-pixel versions of those procs but I think what developers would really need is some new procs.

For example, here's how you might write a punch proc for a tile-based game:

mob/proc/punch()
for(var/mob/m in get_step(src,dir))
m.damage(5)


With tile-based movement you're either standing on the tile next to your target or you're not. Your punch will either hit or miss by a whole tile (or more). With pixel-based movement you need finer control over determining who gets hit, a pixel-based get_step wouldn't necessarily do the trick though.
It could if there were a settings file with customizable pixel steps. As far as range() and get_dist(), I would assume you would do get_dist() as pixel distance and then somehow use that to determine what is in range. The only problem with finding objects in range, is you don't know when to stop--you'd have to check all movables.
Vermolius wrote:
It could if there were a settings file with customizable pixel steps.

I'm not sure what you mean by that.

As far as range() and get_dist(), I would assume you would do get_dist() as pixel distance and then somehow use that to determine what is in range. The only problem with finding objects in range, is you don't know when to stop--you'd have to check all movables.

I'm not too concerned about range. You can still use view, oview, range, and procs like that, they just lack some precision. If you call view(3,src) you'll get a list of all objects in a 7x7 box but the box isn't centered around src, it's centered around the tile that src occupies.

For creating a pixel-based range function I'd probably use the tile-based range or block function to identify all objects in an area that's slightly larger than what you asked for, then use a pixel-based distance function to prune out objects that are too far away. That way to compute pixel_view(96,src) it computes view(4,src) then removes atoms that don't belong in the list.
Yeah, I realized after I posted that, that you probably hadn't removed the tile system.
I just posted a small update to Forum_account.Sidescroller. I added a new file (procs.dm) which contains some helper procs that'll make up for not being able to use get_step. You can do this to open a door in front of the player:

for(var/turf/door/d in src.front(4))
d.open()


front(4) returns a list of all atoms within 4 pixels of src's front side (which is based on the direction you're facing).

These helper procs are simple to make. I'm sure that the few I've created just now won't be completely sufficient, but hopefully this will get the ball rolling and people will contribute their own. If you'd like to request a helper proc or provide an implementation for one you made, you can post on my forum.
Vermolius wrote:
Do you use vectors?

He doesn't use primitives, though, just values :(
Jeff8500 wrote:
He doesn't use primitives, though, just values :(

You've complained about this before but I'm still not sure why you think this is a bad thing. If you want to make the library support a vector datum, just add this code to your project:

mob
pixel_move(vector/v)
..(v.x, v.y)


Just like the library overrides parts of BYOND's default movement system to make something new, you can override parts of my library's movement system to make something new. It might sometimes be beneficial to use vectors in conjunction with the library but some projects might not need it. By not having built in support for a specific vector object you're free to use whatever you'd like: write your own, use a vector library, or don't use a vector datum at all. If the library had support for a specific vector datum you'd lose that freedom.
The reason is, a few years ago someone posted an article about vectors and since then people have been confusing vector mathematics and vector datums. :p
I just basically overhauled your sidescroller library, and I thought I might let you know what I did so you can consider it.

First of all, all atom/movable atoms can have pixel movement w/ collision detection (if they are dense, of course). The client keyboard stuff was separated from /mob and put into /client, which atom/movable/movement() reads (if the key information is there; it's not there for atoms that don't have a client, these atoms have an overridden movement() proc that dictates their movement).

example:
obj
thing
icon = 'Thing.dmi'

pwidth = 13
pheight = 24

density = 1

New()
..()
speed = rand(2, 5)

var
walk_dir = EAST
speed = 2

bump(x, d)
if(d == EAST || d == WEST) walk_dir = turn(walk_dir, 180)

movement()
if(walk_dir == EAST) vel_x = speed
else vel_x = -speed

if(walk_dir == EAST) // these two if()'s check if the obj is about to walk off an edge, and turn it in the other direction if so
var
turf/t = locate(/turf) in getAdjacentAtoms(SOUTH, 1, 1)
turf/t_adj

if(t)
t_adj = get_step(t, EAST)
if(px > (t.px + t.pwidth - pwidth))
if(t_adj && !canBump(t_adj)) walk_dir = WEST

else if(walk_dir == WEST)
var
turf/t = locate(/turf) in getAdjacentAtoms(SOUTH, 1, 1)
turf/t_adj

if(t)
t_adj = get_step(t, WEST)
if(px <= t.px)
if(t_adj && !canBump(t_adj)) walk_dir = EAST

..() // default movement() behavior is to call gravity(), slow movement down, etc


Second, the global movement loop no longer loops through every atom every tick. There is a global list of movable atoms that are currently moving (and thus need their movement() proc called every tick). They are removed from that list when they stop moving (and are added back again if they need to move). When that global list is empty, the movement loop halts. It resumes when a movable atom is placed back into the global list.

Third, I redid the bumping stuff so that there is only a single bump() proc. The difference is that there is a second argument to it that specifies the direction of the bump (north, south, east, west). This makes handling bump events much easier.

I also did a lot of restructuring, because there seemed to be lots of redundancy (get_ground(), get_ceiling(), get_left(), get_right(); I made a single proc that does all of these things in less code)

Fourth, your parallax support does not make the parallax image loop itself. Once it scrolls to the edges, it's just black.

Fifth, the camera is wonky when the player is near the edges of the map. This was fixed.

I think the last thing I did was add a second density variable that allows for directional density. This lets you create platforms that you can jump onto from below, for example.

Numerous other things were done to improve ease of use. You have a nice library here, and after implementing the things I stated above, it became even better & easier to handle. If you want me to send you my code (well, your code ;P) to see exactly everything I changed, I don't have a problem with that. I did remove a lot of stuff that I didn't need for my game (ladders, and sloped atoms, for example), so those things aren't there.

Thumbs up on the library!
Koil wrote:
If you want me to send you my code (well, your code ;P) to see exactly everything I changed, I don't have a problem with that.

Sure! It sounds like you made a lot of good changes. You can post a link here or on my forum. Or you could just post the code on the forum.

I'm not sure I like the change to the movement loop but I'm not thrilled with how it works now. I wanted my implementation to be sufficient (and it is) but I expect it's one of the things people will often end up overriding. Part of this is because you can tailor it to your specific game, part of this is because it could be better to begin with.

In Exordium & Terminus I used the movement proc as the enemy's AI loop. I think as I make more complex enemies it'll become more clear as to how the movement loop should work. Ideally I'd like it to be very easy to recreate the AI for any enemy from the NES Mario Bros. games. This'll probably take a little restructuring and a bunch of helper procs.
Forum_account wrote:
Koil wrote:
If you want me to send you my code (well, your code ;P) to see exactly everything I changed, I don't have a problem with that.

Sure! It sounds like you made a lot of good changes. You can post a link here or on my forum. Or you could just post the code on the forum.

I'm not sure I like the change to the movement loop but I'm not thrilled with how it works now. I wanted my implementation to be sufficient (and it is) but I expect it's one of the things people will often end up overriding. Part of this is because you can tailor it to your specific game, part of this is because it could be better to begin with.

In Exordium & Terminus I used the movement proc as the enemy's AI loop. I think as I make more complex enemies it'll become more clear as to how the movement loop should work. Ideally I'd like it to be very easy to recreate the AI for any enemy from the NES Mario Bros. games. This'll probably take a little restructuring and a bunch of helper procs.

Sorry! Completely forgot to check your response. I'll work on separating my version from the game I'm using it in and package it up and I'll post it on your forum!
Koil wrote:
I'll work on separating my version from the game I'm using it in and package it up and I'll post it on your forum!

Excellent. Thanks!
SuperAntx wrote:
The next step would be to add replacements for some of the commonly used default procedures. It would be much easier to turn the tech demos into real games if things like get_step(), range(), get_dist() and so on worked on a per-pixel basis.

I actually have these done already in a project of mine, though I need to add the capability for them to read for z variables as well, as it currently only returns based on PX and PY