Page 2 of 8

Posted: Tue Jun 14, 2005 4:37 pm
by Zepper
You could use a scanline counting, but this would require good timing. As reference, set up a variable indexed with the timing (in cycles per frame). Every NMI would reset it to zero. As it's indexed properly, you linked it with the Y position of the objects - if any object's present, then analyse its X position in a certain range. You would do this 1 time per line, and killing it if the object is in range.

suggestion

Posted: Tue Jun 14, 2005 4:42 pm
by Guest
This abstracted version (often called a "engine") should allow you design levels using the concept of objects, as opposed to pixels. The key is to get the level of abstraction right.

Agreed. A pixel based collision algorithm would require signficantly more work since it needs to be calculated each and every time.

#define LEFT 1
#define UP 2
#define RIGHT 4
#define DOWN 8
#define UL 16 /*upper left*/
#define UR 32 /*upper right*/
#define LL 64 /*lower left*/
#define LR 128 /*lower right*/


Collision_Detect(int x, int y)
{

int return_value = 0;

if (location (x - 1, y) has an object in it) /*check to the left*/
return_value = LEFT;

if (location (x + 1, y) has an object in it) /*check to the right*/
return_value = return_value BITWISE_OR RIGHT;

... test the remaining directions

return return_value;
}

Posted: Sat Sep 03, 2005 7:01 pm
by Celius
Well, I've figured out a way to collide with the background, I just need to find a better way to actually check for collision with certain objects. Here's my code for collision, I have variables:

Code: Select all

upcol: .ds 1       ;variable for upper collision
dncol: .ds 1       ;variable for down collision
lfcol: .ds 1        ;variable for left collision
rtcol: .ds 1        ;variable for right collision

understand? Okay, and when there's a collision, 1 is added to upcol, dncol, or whatever one. and when there isn't a collision, the value of upcol, etc. is 0. It's kind of like a bit on/off thing where the bit is 1 when on, and 0 when off. okay, here's my code:

Code: Select all

upkey:
       lda upcol
       cmp #1         ;check for collision
       beq stop
       jsr wait_vbl    ; wait two vblanks
       dec cy           ; decrease sprites y position once per 2 vblanks
       rts

stop:
       rts                ; no operation, stop moving.

and that's how it is for every press. I just have it so you can't go past a certain line, and I have a function saying:

Code: Select all

check:
         lda cy        ;sprites y position
         cmp #150
         beq there  ; if sprites y pos is 150, go to there lable
         lda cy
         cmp #150
         bne therea ; if sprites y pos isn't 150, go to therea lable
         rts

there:
        lda #1
        sta upcol
        rts

therea:
        lda #0
        sta upcol
        rts

Yeah, so it just checks for that line of pixels, and nothing else. What's a good method to check for background objects? And I would really appreciate if sample code was supplied. thanks.

Posted: Sun Sep 04, 2005 1:28 am
by Bregalad
The code you supplied is, oh my, incredibly bad.
Collision detection is much easier than it seems. You just have to be sure of what you want to do. It would be pretty different from a top-down adventure game, a side-scroller platformer, or an RPG. Basically, you have to first check if the player is moving or not. Then if moving, check for collision for all direction to be able if the move can be done or not. Eventually, setup the new position if there is no collision. The vertical and horizontal thing should be setup separately, since the player can colisionate only horizontally but not vertically (aka : Jumping on a wall while trying to enter to the wall), or horizontally but not vertically (aka: While just walking on platformers, the hero would automatically fall exept if there is a collision, so it's the case on normal walking).
This may sound a bit confusing, but a platformer needs to always check collision while a RPG doesn't (scince the player won't fall if there is no collision).
But anyway do NOT wait wblanks while checking for collision or you may not go far away. This sould be done DURING the frame.
By the way, checking the sprites position trough the sprite buffer $200-$2ff is also an incredibly bad idea, because you basically have sprites with different size and sprite cycling, well, it's a terrible idea. You anyway should have copies of all position for your sprites in RAM.

Posted: Sun Sep 04, 2005 11:13 am
by Celius
Okay, I don't think the code is THAT bad. It's definetely not what you want to use, but it's not that bad. I'm making a top down pacman style game right now, and I want to know how you can check if the sprites are over a certain tile. like, how would you say this?:

collision:
lda sprite_pos
(some code to
check if it's over
a certain tile)
(then some collision
code)

I really don't know how to do that, because I'm not sure how the sprites position and the placement of tiles are at all related. Any advice?

Posted: Sun Sep 04, 2005 11:34 am
by tepples
The location of the 8x8 pixel tile that corresponds to a particular sprite X location is given by the formula:
tile X = (sprite X + screen scroll X) / 8
tile Y = (sprite X + screen scroll Y) / 8

Code: Select all

  ; convert sprite coords to tile coords
  clc
  lda sprite_x
  add screen_scroll_x
  lsr a
  lsr a
  lsr a
;  lsr a  ; uncomment this if you're using 16 pixel wide metatiles
  sta tile_x
  clc
  lda sprite_y
  adc screen_scroll_y
  lsr a
  lsr a
  lsr a
;  lsr a  ; uncomment this if you're using 16 pixel tall metatiles
  sta tile_y
  ; Take tile_x and tile_y as indices into your map array,
  ; looking up whether the tile at this position is transparent.
  ; The technique for this depends on how you're storing your maps.
You'll usually want to do this for several points on the outside of your sprite's hitbox.

Posted: Sun Sep 04, 2005 11:36 am
by Disch
You should have the decompressed map stored in RAM (or at least, the parts of the map that are on screen or player/object accessable). To keep it easy, the map width should be a power of 2 value (16 or 32 is a reasonable width). Alternatively, you could keep the width the sum of 2 power-of-two values (16+4 = 20, or 16+8=24) but this will make tile lookup a little harder and take a little longer.

The map should probably be stored in "read like a book" fashion, top row stored first, left to right, then 2nd row, then third row, etc, etc.

How a small 4x4 example map might look:

Code: Select all

1111
1001
1101
1111
'1' tiles being the tiles the player cannot move over, '0' being the tiles the player can move over.

This would be stored in RAM somewhere like:

Code: Select all

01 01 01 01 01 00 00 01 01 01 00 01 01 01 01 01
Assuming a tile size of 16x16 pixels, you can easily convert the player's X,Y pixel coords to tile coords by right shifting each four times. Once you have the tile coords, you can lookup which tile the player is currently over with a simple formula:

tile[ (Ycoord * mapwidth) + Xcoord ]

In 6502 you could code that something like:

Code: Select all

LDA Player_Y_coord
ASL A
ASL A  ; multiply by 4 (example map had width of 4)

CLC
ADC Player_X_coord

TAX ; move to index reg

LDA Map_in_RAM, X

; A now contains the tile they're stepping on
When moving, you can check the tile the player will be moving TO and see if they can move there. If they can, then allow them to move, but if they can't, you know they're trying to hit a wall, so stop them from moving.

Posted: Sun Sep 04, 2005 11:43 am
by Bregalad
Okay, I'll show you a sample to give you several ideas.

Code: Select all

GetCollision: ;A=Vert.Pos, X = Horiz. Pos
                   ;Get the collision in an array of 8x8 background block
                   ;The flag saying if the background is set is in a RAM array
                   ;Where each BG block attibute is, let's say, 2 bytes long
                   ;Exit with carry clear if solid, set if passable
asl A
asl A
asl A      ;The array is 8x8 so the vertical should be multiplied by 8
stx Temp
ora Temp ;And added with the horizontal index
tax
lda MapTable,X ;Get the block number
asl A                ;I said the block attibs are 2 bytes long so multiply by 2
tax
lda BlockTable,X ;Load the attribute for the current block
asl A                  ;Let's say that the data is in bit 7 of the first byte
rts                     ;The carry has the value of that bit = slolid or not
Of course, scince your sprite whill have a fixed width, you'll probably have to do more than one check like this, i.e. when walking up, you should check at the extreme left and at the upper-left and upper-right corner of your sprite, and the same principles apply to all 4 directions. I think you'll basically need a different piece of code for each 4 directions, and all of them will call similar routines like the one above.

Edit : Wow, both Dish and Tepples posted their answer while I was writing mine, so you definitely have different example from different peple explaining things differentely to you. No excuse, now :wink:

Posted: Mon Sep 05, 2005 10:34 am
by -tokumaru-
Celius wrote:

Code: Select all

upkey:
       lda upcol
       cmp #1         ;check for collision
       beq stop
       jsr wait_vbl    ; wait two vblanks
       dec cy           ; decrease sprites y position once per 2 vblanks
       rts

stop:
       rts                ; no operation, stop moving.
and that's how it is for every press.
OK, Celius. You are doing something really wrong here. You should NEVER wait for vblanks inside your keypressing routine. Why? Well, mainly because there are (or at least should be) more stuff happening in the screen besides the movement of the player. You are literally stoping EVERYTHING (for two frames!) while your player moves. The music (if you have any) will hang, enemies that should be moving around will stop, etc etc.

In order for a game to work, your engine CAN'T revolve around 1 object, in this case, your player. You must let the frames flow, and every frame check if there is anything to do with each object in the screen.

In your case, you shouldn't have an "upkey" routine. I think you should have 2 "acceleration" variables. One horizontal and one vertical. If the up key was pressed, set your vertical "acceleration" to -1 (so your player goes up), and increment your acceleration variables to the player's coordinates every frame. Don't just hang the game while the player is moving, he/she is not the only thing that needs attention in the game.

And if you NEED to restrict the player's speed to less than 1 pixel per frame, just add a fraction of the acceleration to the player's coordinates. If the minimum you player can walk is half a pixel (as you said, 1 pixel in two vblanks) you add half the value in the acceleration variables to the player's coordinates.

Now, after moving, you check if the object/player collided agains anything. If it did, move it back to where it was.

And as Bregalad said, don't do your game logic during vblank. In most cases, there is not enough time, unless your game is REALLY simple. You should do the logic OUTSIDE of vblank, and wai for vblank to DRAW the stuff you just computed to the screen.

Sorry if I didn't supply you with any sample code as you'd like, but the ideas I presented here are very simple, if you read carefully and try to pictures the stuff I'm saying I'm sure you'll understand.

-tokumaru-

Posted: Mon Sep 05, 2005 11:22 am
by -tokumaru-
Ok, it may be a little hard to understand at first, so here is some pseudo-code. Start this OUTSIDE of vblank:

Code: Select all

//The first thing you do is read the keys. Do this however you want.

//Clear acceleration.
AccelX = 0;
AccelY = 0;

//Set acceleration according to keys pressed.
if (keyup) then AccelY = AccelY - 1;
if (keydown) then AccelY = AccelY + 1;
if (keyleft) then AccelX = AccelX - 1;
if (keyright) then AccelX = AccelX + 1;

//Move the player horizontally.
PlayX = PlayX + AccelX;

//Check for collision with background (this is just one way of doing it...).
If (Map[PlayX div 16, PlayY div 16] <> 0) then PlayX = PlayX - AccelX;

//Move player vertically.
PlayY = PlayY + AccelY;

//Check for vertical collision with background.
If (Map[PlayX div 16, PlayY div 16] <> 0) then PlayY = PlayY - AccelY;

//Move any other objects you may need to move now.

//Now you wait for vblank and draw the stuff to the PPU. Start over.
Of course the collision detection here is very basic, I'm even ignoring the concept of bounding boxes, but implementing it woudn't be very hard.

Also, in this case you're limited to 1 pixel per frame. To move less than that, I guess you'd have to increase the precision of your player's coordinates (double it) and them divide it by 2 when placing the graphics on screen. You can't add half the acceleration (as I said before) as it will always be 0 when you divide 1 by 2 and the player will never move! I was thinking about something else, in my game I divide the acceleration to have my player accelerate slower. I mixed things up.

Also, in my game I don't clear the acceleration, I decrease it little by little if the player is not pressing a key in that direction. And I set limits to it (so the player does not move too fast), and then it really works like acceleration.

-tokumaru-

Posted: Mon Sep 05, 2005 11:28 am
by Bregalad
I totally agree, however I have to discuss about one point.
You said you have two variables, one for VSpeed and HSpeed.
On the game I'm writing now, I do it on a very different way.
I check for what the player does (in a flag that could handle each player action, stuff like staying, walking, prepare to attack, attack, etc...). Then, if the action is walking (and this would be probably the only one where the player would move), check for witch directions are pressed on the joy-pad. Eventually, if the player moves vertically, check for if it's up or down (one button will override the other one if both are pressed, that is impossible on a NES joypad), and then it's position will be decreased/increased by 1 on odd frames and 2 on even frames, then the collision will be checked vertically. Now, do the same horizontally (so the player can move diagonally).

This is slightly different, because there is no speed variables at all, and the player does move at a constant rate (I kinda prefer this, since the player is more responsive, however I'm not against a little acceleration like in Mega Man games, but a gigantic acceleartion like in SMB makes the whole game hell). Do you think I should change that, or is it okay as it ?

Posted: Mon Sep 05, 2005 11:49 am
by -tokumaru-
Bregalad wrote:I totally agree, however I have to discuss about one point.
You said you have two variables, one for VSpeed and HSpeed.
On the game I'm writing now, I do it on a very different way.
I check for what the player does (in a flag that could handle each player action, stuff like staying, walking, prepare to attack, attack, etc...). Then, if the action is walking (and this would be probably the only one where the player would move), check for witch directions are pressed on the joy-pad. Eventually, if the player moves vertically, check for if it's up or down (one button will override the other one if both are pressed, that is impossible on a NES joypad), and then it's position will be decreased/increased by 1 on odd frames and 2 on even frames, then the collision will be checked vertically. Now, do the same horizontally (so the player can move diagonally).

This is slightly different, because there is no speed variables at all, and the player does move at a constant rate (I kinda prefer this, since the player is more responsive, however I'm not against a little acceleration like in Mega Man games, but a gigantic acceleartion like in SMB makes the whole game hell). Do you think I should change that, or is it okay as it ?
I think your method is fine. Since your game is RPG style, and you don't move diagonally (in many RPG's you can't, right?), it works quite well as you described. The method I described (in better detail in my second post) is more suited to a platformer, where you can have any combination of horizontal and vertical speeds. But when I simplified it for Celius's needs it didn't look all that smart! =)

You're absolutely right when you say you only check for collisions in the direction you're walking. You can do that with the method I use, just decide what to do based on the speed values (you're right, maybe it is better to call them SPEED variables). If it is 0, don't check for collisions. If it is negative, look for collisions at the top/left, and if it is positive, look for collisions at the bottom/right.

In the example I posted, opposing keys will cancel each other, even though it is not possible on a real NES pad! Most people run this stuff in emulators, anyway! =)

The method I described is really good for games with actual acceleration, like Mario or Megaman, as you said. Just set your limits and scales carefully and you can do both with this technique, I guess.

-tokumaru-

Posted: Mon Sep 05, 2005 12:05 pm
by -tokumaru-
Just adding one more thing on Bregalad's post:

I prefer to have the two directions variables in addition to the flags, as opposed to only the flags. That is because, in my platform game, walking does not cancel other actions. I might be walking/running -"moving" is a better word, actually- at the same time I'm jumping, attacking, etc.


In most games of the kind you're making you can't, say, attack and walk at the same time, right? If you press the attack button, while walking, the player will stop, attack, and then continue walking, right? If so, the flag for each action method works just fine. The "attack" flag must have a higher priority though, right? And the "attack" action is also affected by the direction the player is headed to, isn't it? So you know where the impact of the attack should happen... You must keep track of the direction of the player (in a separate variable, probably) even if he is not moving, so the other actions also work in the direction they should... right?

-tokumaru-

Posted: Mon Sep 05, 2005 12:37 pm
by Bregalad
I can and will move diagonally, because the left/right move and up/down move are done in parallel. This is not a RPG, but Action RPG, so good controls are definitely needed. I decided to do exacly as you explain in the second post, so attack button is checked even while walking, and if active, the player will stop to walk, attack, and begin to walk again. As you described, the direction the player is facing is independent from where it moves, but is redirected if the player is no longer pressing the corresponding arrow. So, you can move up-left while watching up or while watching left, both are possible.

I can't imagine how bad would Mega Man or Contra be with this method, however. But Crystalis would surely be better if the hero would stop to walk while attacking. I found that terribly anoying.

Posted: Mon Sep 05, 2005 12:58 pm
by -tokumaru-
Bregalad wrote:I can and will move diagonally, because the left/right move and up/down move are done in parallel.
Oh, I see. But it will be on exact diagonals, right? Since your speed is fixed... I say that because, in a platformer, you can, really, move in any combination of vertical and horizontal speeds. I guess it's best described as an "all-way scroller" then an "8-way scroller"! =)
This is not a RPG, but Action RPG, so good controls are definitely needed. I decided to do exacly as you explain in the second post, so attack button is checked even while walking, and if active, the player will stop to walk, attack, and begin to walk again.
But the way you did before worked as you wanted, didn't it? That thing I wrote is more usefull if you do want to attack and keep moving, and I can see this is not your case. Not that it won't work for you or anything, you can do what you think is best, and I think your new way will work just as well! =)
As you described, the direction the player is facing is independent from where it moves, but is redirected if the player is no longer pressing the corresponding arrow. So, you can move up-left while watching up or while watching left, both are possible.
I didn't quite understand this... you can move towards one direction while facing another? I've never seen a game do this... Well, at least not in the NES, where we have limited controls... In newer games it is quite common beeing able to walk/move in one direction while shooting at the other and all...
Anyway, I just think I didn't understand what you said! =)
I can't imagine how bad would Mega Man or Contra be with this method, however. But Crystalis would surely be better if the hero would stop to walk while attacking. I found that terribly anoying.
Hehe! I did a Megaman clone for the PC years ago where the player had really bad limitations. I had only horizontal acceleration, the vertical speed was fixed! Looked pretty bad! And to save graphics, the player could only shoot standing. It wasn't very smooth, indeed. With tiled graphics it is easier, since you could keep the legs moving AND keep the arm straight shooting, but I didn't have a clue about tiled graphics back then.