Log In  


I'm trying to make a platformer that just uses pixel colors as a way to check for the ground/walls of a level, no sprite flags. At first it seems to work, the guy (stick) moves left/right and jumps on the initial ground just fine. But then when you jump up on the platform to the right and either walk or jump off back to the left, he sinks into the ground.

Sorry, this cartridge is not currently available.
by
Cart #23455 | 2016-06-23 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
2

See code below, but what I'm doing is checking the pixel immediately below the stick to see if it's a ground color and if so, stop applying the gravity. Seems simple enough...made sense to me but I'm clearly missing something.

I'm happy with the jumping action, it's nice and smooth and such but landing seems to be an issue. If anyone has suggestions for my code or just insight in general, I appreciate it. Making a platformer is new to me...I usually make shmup games :)

NOTE: Code as-is is not checking for left/right collisions, just ground for jumping.

t=0
colors={wall=7,air=0,player=8}
debug=false

--
-- player
--
positionX = 40;
positionY = 89;
velocityX = 2;
velocityY = 0;
gravity = 0.5;
onGround = false;

function _update()
    btnright=btn(1)
    btnleft=btn(0)
    btnjump=btn(4)

	--horizontal movement
    if btnleft then positionX-=velocityX end
    if btnright then positionX+=velocityX end

	if positionX>127 then positionX=127 end
	if positionX<0 then positionX=0 end

    --jump, apply velocity
    if btnjump and onGround then
        velocityY = -3.5;
        onGround = false;
    end

	--calc in gravity and add to Y
    velocityY += gravity;
    positionY += velocityY;

	--check pixel below player to see if it's a wall color
	local below=flr(positionY+1)
	if pget(positionX, below)==colors.wall then
        positionY = below-1;
        velocityY = 0;
        onGround = true;
    end

	debug=pget(positionX, below)

    t+=1
end

function _draw()
    cls()

    line(positionX,positionY, positionX,positionY-2, colors.player) --draw stick up from foot
	pset(positionX,positionY, 10) --foot

	--ground
    rectfill(0,90, 128,128, 7)
	rectfill(100,82, 128,128, 7)

    print(debug,0,120,1)
	print(positionX..", "..positionY, 90,120,2)
end

2


The problem is likely from the velocity being a decimal, rather than a whole number. When falling from distances that are odd-numbered, you'll get fractional values and I assume rounding errors.

The typical way to do this is to take your current position and add the velocity as an offset, then scan from A to B and find the last valid position it could go to (so it doesn't clip through a wall). If you set up your methods correctly, it will be very easy to translate it from vertical logic to horizontal

I didn't have a chance to look at your code in depth, so I might be wrong here.


When you jump down, your VELOCITYY is probably exceeding 1 pixel/update when you fall past the ground level you started at, since you'll fall longer and build up more speed. That lets you embed yourself more than 1 pixel into the ground before you do your collision test. You then do your collision test, see that you've hit, and back up 1 pixel, which leaves you still in the ground.

Your collision test should really check each pixel between the old position and the new iteratively and stop when it hits ground. That, or limit your downward velocity to one pixel/update.


Hmmm...okay, I can try to give that a shot. So rather than doing X/Y changes directly, do them in a temp variable and check, then work back from there before the change is applied? I think I got it, I'll give it a try and see how bad I munge it up.


I wouldn't work backwards. Go forwards from the previous position. That way you collide with the first pixel that's in the way. Otherwise you have to see you've collided and then back up and look for the first clear pixel, which is double the work/code.


Also, don't manually back up when you hit a ground pixel. Just set your position to the last clear pixel you tested. Otherwise doing something like decrementing Y can put you off-trajectory and possibly inside a different ground pixel.


Here's what I came up with and it seems to be working. I jump all around and he never goes below the level, so that's good. Still no X collision checking yet. But please give what I figured out a review and let me know if it's worthy solution.

I'm not having it back up at all. If the destination is the ground color then I just skip adding it to the position.

Cart #23471 | 2016-06-23 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

positionX = 40;
positionY = 89;
velocityX = 2;
velocityY = 0;
gravity = 0.5;
onGround = false;
dx=positionX
dy=positionY

function _update()
    btnright=btn(1)
    btnleft=btn(0)
    btnjump=btn(4)

	--horizontal movement
    if btnleft then dx=positionX-velocityX end
    if btnright then dx=positionX+velocityX end

	if dx>127 then dx=127 end
	if dx<0 then dx=0 end

    --jump, apply velocity
    if btnjump and onGround then
        velocityY = -3.5;
        onGround = false;
    end

	--calc in gravity and add to Y
	if not onGround then

	end
	velocityY += gravity;
    dy=positionY + velocityY;

	--check pixel below player to see if it's a wall color
	if pget(dx, dy)~=colors.wall then
		positionY=dy
	else
		velocityY = 0;
        onGround = true;
	end

	positionX=dx

	debug=positionX

    t+=1
end


It's an interesting way to solve it (deleting code/work is generally the goal), but it does have a flaw.

What you're doing is basically stopping it at the previous position, resetting the velocity due to the collision, and then continuing. The next frame or two accelerates again towards the ground (since you're not on it) and you end up landing correctly since the new velocity is sufficiently small. That often produces a brief hiccup in the motion when you're just about to hit the ground after jumping down.

Edit: It also allows you to jump again before you actually hit the ground. Hold down the jump button while running around and you'll note that you seldom actually touch the ground.


But isn't that the last clear spot?

I just added some X collision stuff (locally) and am finding that the dude won't jump up at all if he's right next to a wall. It's weird. The dy variable should just be whatever is above him, which is nothing; same with dx.

And found that if you happen to jump just right, you'll stick to the wall above the ground...highlighting your point that it just stops and doesn't apply any more gravity.

Hmmm...well I'm closer, at least. So frustrating because it's so close, been trying to figure this thing out most of the day too. Thanks for the insight, hopefully something will click for me soon.


It's the clear spot you were in during the previous frame, but if your speed this frame exceeded one pixel, then it might not be the last clear spot before the collision.


How do you check space in between if the dx/dy is all you have and it's greater than 1px/frame?

I'm calculating the next position into dx/dy, which is based on a speed greater than 1px/frame, like you said. That dx/dy is the next position the player will land, which could be the ground, which is bad. So dx/dy isn't what I want, and I don't want to stop the dude at his current x/y because it's not at the ground yet...so what other option do I have? How would I know what the next clear spot is if all I have a speed bigger than 1?


Well, here's where I ended up after more playing around. Only weird thing I notice is one pixel weirdness when he jumps into a wall, he dangles 1px above the ground...I know why, kinda, but not sure how to solve it, and not sure if that 1px bothers me enough or not. I think I'll take this and run with it for my game and then if it bothers me see what I can do.

Thanks for all the help and direction...meant a lot and got me this far :)

This does now have X collision checking for walls. I also reduced the player speed to 1px rather than 2px. Probably helps the math a little bit too.

Cart #23482 | 2016-06-23 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

t=0
colors={wall=7,air=0,player=8}
debug=false

--
-- player
--
positionX = 40;
positionY = 89;
velocityX = 1;
velocityY = 0;
gravity = .5;
onGround = false;
dx=positionX
dy=positionY

function _update()
    btnright=btn(1)
    btnleft=btn(0)
    btnjump=btn(4)

	dx=positionX

	--horizontal movement
    if btnleft then dx=positionX-velocityX end
    if btnright then dx=positionX+velocityX end

	if dx>127 then dx=127 end
	if dx<0 then dx=0 end

    --jump, apply velocity
    if btnjump and onGround then
        velocityY = -3.5;
        onGround = false;
    end

	--calc in gravity and add to Y
	if not onGround then

	end

	velocityY += gravity;
    dy=positionY + velocityY;

	local pxcheck=pget(dx, dy)
	local pxbelow=pget(positionX, flr(dy)+1)

	--check pixel below player to see if it's a wall color
	if pxcheck==colors.wall and pxbelow==colors.wall then
		velocityY = 0;
        onGround = true;
	else
		positionY=dy
	end

	if pget(dx, positionY)~=colors.wall then
		positionX=dx
	end

	debug=pxbelow

    t+=1
end



[Please log in to post a comment]