I'm at a loss trying to decipher some code to better my understanding of core principles. Using the fantastic Advanced Micro Platformer by @mhughson as a subject of study I was reading into how the engine detects horizontal collision for the player. It does this through a collide_side function
function collide_side(self) local offset=self.w/3 for i=-(self.w/3),(self.w/3),2 do --if self.dx>0 then if fget(mget((self.x+(offset))/8,(self.y+i)/8),0) then self.dx=0 self.x=(flr(((self.x+(offset))/8))*8)-(offset) return true end --elseif self.dx<0 then if fget(mget((self.x-(offset))/8,(self.y+i)/8),0) then self.dx=0 self.x=(flr((self.x-(offset))/8)*8)+8+(offset) return true end -- end end --didn't hit a solid tile. return false end |
So I'm trying to make sense of this, and I can't seem to parse it in a way that makes sense. Breaking it down it assigns a local value offset to 2.66 roughly, because self.w is 8. And then it runs a for loop beginning at -2.66 through 2.66, with a "stride" of 2? So it counts up from -2.66 to 2.66 in increments of 2, and adds the current i to the y value? I assume this is checking for collision along the y axis of the sprite from top to bottom, but I cannot figure why it seems to check the y coord + -2.66, -.66, etc. So I assume I am misunderstanding something big.
And then it sets dx to 0, and then sets the player x to what I assume is the start of the wall tile. But I can't see where it accounts for player acceleration to adjust the end value, or how it compensates to avoid a "clip" into the wall.
I'm at my wits end trying to decipher this and further my understanding of how this math works. I've resorted to pen and paper drawing out different runs through this function, and it just seems like magic.
I'm wondering if anyone can help me, or otherwise direct me towards something to help me with these fundamentals. Happy Holidays!
Edit: Figured I'd link to the cart itself as well:
Hi @dad Jr.
Thanks for taking the time to try out the platformer starter kit!
I've gone through and added much more detailed comments. I hope this can better explain what it is doing. (I removed the commented out code, and fixed the tabs as well).
The whole thing is actually based loosely on this document outlining the collision in McKids for the NES (search for "Now for the fun part. NOT!" to get to the collision section): https://games.greggman.com/game/programming_m_c__kids/
-- Check for collision on the sides of "self", and shift "self" back -- to the edge of the wall in the case that they do hit something. function collide_side(self) -- This function works by checking a few points along the right and left edge of the -- object (self). It starts at the top and works down to the bottom. -- Usually you might check for collision at the edge of the object (which would be -- an offset of width/2), but the sprite doesn't take up the whole width of the -- object, so instead offset by a 3rd, giving a little bit of overlap with the walls. local offset=self.w/3 -- This loop will check collision every 2 pixels from top to bottom of the object, both on the -- left and right edge. -- 'i' will be the vertical position, while the x offset is fixed ("offset"). for i=-(self.w/3),(self.w/3),2 do -- a lot happens here so I'll try to explain it in chunks... -- self.x+(offset): move to the right edge of the object.strokeWidth -- (self.x+(offset))/8: divide by 8 (the width of a tile) to put this into "map space". -- (self.y+i)/8): check a position on the edge, from top to bottom. Again, put it into "map space". -- mget(...): get the tile id that this point is inside of. -- fget(..., 0): Check if flag 0 is set on the tile, which means "solid" for this engine. if fget(mget((self.x+(offset))/8,(self.y+i)/8),0) then -- A collision has been detected... -- Stop the horizontal momentum (dx, and dy represent velocity). self.dx=0 -- self.x+(offset))/8: Get the x position of the tile we hit in map space. -- flr(...): truncate it down to ensure that it is an actual tile number. -- ... * 8: now put it back into world space, but because it was truncated before, this will put it on the left edge of the tile. self.x=(flr(((self.x+(offset))/8))*8)-(offset) -- A collision was found, no need to continue... return true end -- This is the same as above, but checking the left edge instead. if fget(mget((self.x-(offset))/8,(self.y+i)/8),0) then self.dx=0 -- Same as above, but because we are check the left edge of the object, we want to butt it -- against the right edge of the tile, so there is an extra +8. self.x=(flr((self.x-(offset))/8)*8)+8+(offset) return true end end --didn't hit a solid tile. return false end |
@mhughson thanks for taking the time to add additional comments and the link to M.C. Kids.
@mhughson Thank you so much for your help and additional info. It's easy for me to hit a wall with this stuff and stop working on it, so I really appreciate you taking the time. Your breakdown really helped me get under the concept.
[Please log in to post a comment]