I know how to move sprites and do backgrounds and all that, but how do you make an object collide with another one? I couldn't really find any info on it, so can someone help me? thanks.
Well, if you know how to move sprites, you know their position, correct? I'm unsure about what do you plan but... anyway, keep track of sprite #0 - if it's a game character (or whatever) and you wanna collision, there are 2 points:
A. Sprite x Sprite collision = you could keep track of their X,Y positions in RAM 200-2FF as example. Once X1 = X2 and Y1 = Y2 (in a latency of S pixels, where S is the size of your desired sprite), you got a strike and triggers the event.
B. Sprite x Background collision = even if you use sprite #0 hits to detect solid background pixel, it's useless most of times. This way, you must read the nametable (a tile in X,Y position) and compares with the sprite - it would had attributes (as solid, air, spikes...). Another RAM region would get the things working good.
I'm sorry, try ASM code by yourself. Get the idea and test it.
First, given two rectangles specified by left, right, top, and bottom, determine whether they overlap. Can you do that?
Then for each game object, construct its bounding box. (Normally, games will leave a few pixels outside the bounding box to compensate for the fact that sprite cels aren't rectangular.)
For each pair of game objects, compare their bounding boxes to see if they overlap.
I didn't really understand what you were saying there Fx3, though it's probably completely logical, and I'm just bad at interperting things into my own head. I have an idea to tell it when the object moving is in tile #99, it will disable the RIGHTKEYdown function. But I don't know how to define a variable in RAM for a tile #, because you can only say something like this:
Something = $80
you couldn't say this:
Something = #80
You can only define variables with hex crap, you can't do it with decimal, and that sucks! I think I'm using a crappy assembler, though, I'm using Nesasm, and I heard that sucks. I'm probably not making sense, so I'm gonna stop babling now.
Sorry, ignore that thing above. This is my code:
Code:
Collide:
lda Y_Pos
cmp #100
beq NOTHINGdown
lda X_Pos
cmp #99
beq NOTHINGdown
I know that that isn't correct by the way. I don't know how to get it to stop when it's at location 99,100 if 100 is Y and 99 is X. It of course stops at in the column where X= 99, and in the column where Y= 100. Ya know what I mean? I can't think of a better way to explain it. How do you get it so it only stops when X= 99 and Y=100?
Please, you must clarify what you want to do. Go read
again what I and tepples wrote about it.
Sorry I'm not clear enough about what i want. I am trying to make a collision with my moving sprite, and a still sprite. It has the effect of a background collision, where when you hit the sprite, you stop moving. The still sprite basicly acts as a block like on mario. Sorry if I'm annoying everyone with my dumb questions...
These questions aren't dumb, just kinda hard.
I'd say that collision detection is one of the trickier things to code. At least for for sprite-to-bg. Sprite-to-sprite is easier and a good place to start, since the sprites are on the same coordinate system to begin with.
What you need to do to use bounding boxes like tepples said, is to do some range checking. This probably isn't an ideal example, but here's how I do this in Munchie Attack.
notes:
player's "mouth" sprite starts at $204
food objects start at $240
the @itsblank label is where it goes for 'no hit'.
the BCC/BCS branch after CMP is used to do greater/less than checking. For example, food objects are coming from the right, so the food's coordinate has to be less than the player's right edge, and greater than the player's left edge to count as a hit.
The values added and subtraced change the size of the box.
Code:
hit_detection:
ldx #0
@hitloop:
lda $241,x
sta tiletype
bne :+
jmp @itsblank
:
lda $243,x
sec
sbc #8
cmp $207
bcc :+
jmp @itsblank
:
clc
adc #24
cmp $207
bcs :+
jmp @itsblank
:
lda $240,x
sec
sbc #15
cmp $204
bcc :+
jmp @itsblank
:
clc
adc #30
cmp $204
bcs :+
jmp @itsblank
:
lda #0 ; if it makes it all the way here, it was hit
sta $241,x
sta $245,x
sta $249,x
sta $24D,x
Also see here for some info about sprite-to-bg collisions:
http://www.greggman.com/mckids/programming_mc_kids.htm
my guess is that you are doing something like this:
byte check_for_collision(
byte sprite_1_x, int sprite_1_y,
byte sprite_2_x, int sprite_2_y,
byte x_distance, /*max X distance needed for collision*/
byte y_distance /*max Y distance needed for collision*/
)
{
byte x_diff = sprite_1_x - sprite_2_x;
if (x_diff < 0)
x_diff = -x_diff;
if (x_diff > x_distance)
return(0); /*no collision*/
byte y_diff = sprite_1_y - sprite_2_y;
if (y_diff < 0)
y_diff = -y_diff;
if (y_diff > y_distance)
return(0); /*no collision*/
return(1); /*collision happened*/
}
Translating to assembly language should not be that difficult.
I wouldn't have trouble with this if there were some way to say this:
lda Y_Pos
cmp #100
and
lda X_Pos
cmp #93
bne NOTHINGdown
When I say and, I don't mean the actuall command. Is there some way to say only go to NOTHINGdown, if Y_Pos = 100 and X_Pos = 93?
Celius wrote:
When I say and, I don't mean the actuall command. Is there some way to say only go to NOTHINGdown, if Y_Pos = 100 and X_Pos = 93?
Yeah. You need to change the branch condition and/or destination of the first check. What comes to mind for me is this:
Code:
lda y_pos
cmp #100
bne skip
lda x_pos
cmp #93
beq NOTHINGdown
skip:
Thank you! But is there a simpler way to do this? because if you're making a platformer with about as many platforms as say Metroid, You probably wouldn't want to do all that for every little pixel, if you know what I mean. Is there a way to write to the object, instead of the pixel?
Oh man, you should have seen the code for a PC sidescroller I tried to write when I was first learning C. I used fractional values for the player position and I think my "move until hit obstacle" tried every single sub-pixel position. A later version found the intercept of the movement vector. Still all major overkill; this wasn't a physics simulation that required absolute accuracy.
Draw a picture and think about the minimum work necessary to find if movement is possible, and how far. Then find simplifications that don't add much more work than the minimum. Be sure to abstract the problem to only the essential details.
Also, find a way to enjoy experimenting with 6502 programming techniques. It really helps to write simple debugging output routines to print a byte (or find an emulator with a good debugger), so you can write short test code and see the results.
Combining the responses already given in another way, here's my $0.02: You obviously don't want to write custom code to detect collision between every object on every map of every level of your game. As blargg suggests, you should try to abstract this problem so that it is more general. 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. The more abstract it is the more general your engine is, but it also becomes more difficult to implement it. You want it abstract enough that it simplifies your design, but not so abstract that it does more than you need.
Try prototyping on a PC first. Get collision detection working in pure C code, then reduce the algorithm to what a 6502 can handle.
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.
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;
}
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:
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:
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:
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.
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.
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?
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:
; 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.
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:
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:
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:
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.
Okay, I'll show you a sample to give you several ideas.
Code:
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
Celius wrote:
Code:
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-
Ok, it may be a little hard to understand at first, so here is some pseudo-code. Start this OUTSIDE of vblank:
Code:
//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-
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 ?
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-
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-
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.
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"! =)
Quote:
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! =)
Quote:
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! =)
Quote:
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.
Quote:
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"! =)
Well, in a platformer you basically don't directly control the vertical position, but only while jumping, falling, etc...
Quote:
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! =)
Yeah, it works fine.
Quote:
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! =)
Well, the player just face a direction that is pressed, the first one detected. If you're walking diagonally, I didn't make my player's sprite have 8 frames, so of couse you can face left then go up-left, or face up then go up-left to. This will make the player moving in the same direction, but not facing exactly the same direction.
Okay, I have to say something. I only did the Vblank wait because When I moved my player without it, She moved like 10000 miles an hour, and you saw 3 frames with her in it as she crossed the screen. That was stupid. How do you fix that? And I got the impression that I was using excuses saying that no one explained things well from you bregalad. I quote you "tepples and Disch wrote their posts while I was writing mine. So you have different approches at answering your questions. No excuse now
." Thank you for being so supportive, Bregalad, I appreciate it. Like now I HAVE to know how to do background collision, because there's no excuse for me now. I actually am still trying to understand this. I was going to mention that I don't know how to do RLE compression, and I just have 1 screen levels that were made with NSA. So I don't have a map in ram. Yeah, I'm at kind of an akward stage here, because I know all of 6502, and I know what the NES can do, but some how I just can't come up with good code. And everyone thinks that I'm some newbie who doesn't really know anything, and has to make new forums that end up being 9 pages long about every aspect of the NES, and I get forwarded to the 6502 for newbies documents. I really do need to get better at anding things and oring things, because I never really do that stuff. Well, I'll understand this some day, I'll come back and ask about it when I'm not so tired and in such a crap mood. so yeah, bye.
Bregalad wrote:
Well, in a platformer you basically don't directly control the vertical position, but only while jumping, falling, etc...
Yes, yes! Gravity (well, simulated gravity) takes care of positive Y speed, if there isn't anything (ground, platform, object, etc) right below the player. In my game, for the player to jump I do nothing more than just set the Y speed to a big negative number (of course I check if the player isn't already in the air -jumping or falling-, or I would have multiple jumps!), wich will cause the player to suddenlly move up, and deaccelerate back as an effect of the simulated gravity. It works quite well.
Quote:
Well, the player just face a direction that is pressed, the first one detected. If you're walking diagonally, I didn't make my player's sprite have 8 frames, so of couse you can face left then go up-left, or face up then go up-left to. This will make the player moving in the same direction, but not facing exactly the same direction.
Oh, I got it now, you have 8 directions but graphics for 4 of them, so diagonals use the same graphics as when you move to the sides. So, there are 8 directions in wich you can move, but 4 directions in wich you can do the other stuff (face, attack)... did I get it right now?
Celius wrote:
Okay, I have to say something. I only did the Vblank wait because When I moved my player without it, She moved like 10000 miles an hour, and you saw 3 frames with her in it as she crossed the screen. That was stupid. How do you fix that?
That must be because you checked for the keypress multiple times during the same frame. Maybe you could undo that "wait two vblanks thing" and do the following:
-after you're done with all the calculations set a "frame done" flag;
-loop forever while this flag is set;
-in your NMI routine, you check if this flag is set. If it is, draw what you need to the PPU;
-clear the "frame done" flag and return from the NMI;
This will cause your loop to end, and the program will proced to the calculations for the next frame. This way your logic will only run once per frame, and the player will not move ridiculously fast! =)
Quote:
And I got the impression that I was using excuses saying that no one explained things well from you bregalad. I quote you "tepples and Disch wrote their posts while I was writing mine. So you have different approches at answering your questions. No excuse now
." Thank you for being so supportive, Bregalad, I appreciate it. Like now I HAVE to know how to do background collision, because there's no excuse for me now. I actually am still trying to understand this. I was going to mention that I don't know how to do RLE compression, and I just have 1 screen levels that were made with NSA. So I don't have a map in ram.
NSA will take only care of the graphics. You must have a map in memory where you can check what is solid and what is not. If your game is REALLY simple, you can use NSA's output for that, but generally you won't. In fact, you should draw to the screen based on your RAM/ROM map, and avoid NSA alltogether. You said it is a pacman-like game. So you probably just have two cases: wall and no wall. Read your map and draw the appropriate tiles to the screen. You don't need NSA at all.
NSA is good when you have to draw that complex title/intro screen scene, where there is no actual logic behind, it's just a bunch of unrelated tiles placed in a order with no logic at all, so it would be difficult to draw it directlly with code.
Quote:
Yeah, I'm at kind of an akward stage here, because I know all of 6502, and I know what the NES can do, but some how I just can't come up with good code. And everyone thinks that I'm some newbie who doesn't really know anything, and has to make new forums that end up being 9 pages long about every aspect of the NES, and I get forwarded to the 6502 for newbies documents. I really do need to get better at anding things and oring things, because I never really do that stuff. Well, I'll understand this some day, I'll come back and ask about it when I'm not so tired and in such a crap mood. so yeah, bye.
I don't think you're stupid or a newbie. In fact, I admire you. You are persistent and a person of action. I, for instance, am always talking about game logic and design, but it's all theory. I talk and think a lot, but have actually coded very little. But you, you go straight to the action, even not knowing how you're going to do some things, but you go and just try. That's why I admire you.
I probably have had much more contact with the game programming world than you have, but you're probably going to have much more to show as result of your work in the end.
I know you understand 6502 assembly, programming logic and you're not a newbie. The only problem is you're not thinking as a GAME programmer yet, you're still thinking in a very specific way, and refuse to find global solutions to your problems. You must not worry about "how will the player move correctly" and disregard completely all other things (as you did with the two vblank thing), you must worry about "how will ALL objects move correctly at once" wich is what happens in a real game. No enemy waits for the player to walk. Enemies walk at the same time as the player does.
Eventually you'll be used to thinking as a game programmer instead of as a game player. But KEEP ASKING, and reading carefully what we say to you! You must have contact with it, even if you don't understand everything we say.
-tokumaru-
-tokumaru- wrote:
Yes, yes! Gravity (well, simulated gravity) takes care of positive Y speed, if there isn't anything (ground, platform, object, etc) right below the player. In my game, for the player to jump I do nothing more than just set the Y speed to a big negative number (of course I check if the player isn't already in the air -jumping or falling-, or I would have multiple jumps!), wich will cause the player to suddenlly move up, and deaccelerate back as an effect of the simulated gravity. It works quite well.
You man putting a value ($f8 for example) in vertical speed variable, so make the player goes up, then decrease it regulary, to eventually stop the decreasing stuff at the maximum value ($08 for exapmple) so the player won't be falling faster more and more, right ? Does it looks good ?
Quote:
Oh, I got it now, you have 8 directions but graphics for 4 of them, so diagonals use the same graphics as when you move to the sides. So, there are 8 directions in wich you can move, but 4 directions in wich you can do the other stuff (face, attack)... did I get it right now?
Yeah, you got it. You'll show when the game will be released, anyway (I hope !).
Celius : You definitely needs some more experience at programming, but scince you got all basic stuff about coding, it would be easier. Just keep how a main game structure should optimally work, I actually also had problems like this stuff (however not at that point) in my beginning. I think a doccument should be written to hanlde optimal programming for begineers, instead of just technical stuff.
Quote:
Yes, yes! Gravity (well, simulated gravity) takes care of positive Y speed, if there isn't anything (ground, platform, object, etc) right below the player.
I swear that in games like Castlevania, when you are standing on something it's still increasing your Y velocity (up to some maximum) so that when you walk off a platform, you fall very quickly.
Bregalad wrote:
You man putting a value ($f8 for example) in vertical speed variable, so make the player goes up, then decrease it regulary, to eventually stop the decreasing stuff at the maximum value ($08 for exapmple) so the player won't be falling faster more and more, right ? Does it looks good ?
Hum.. not exactly... It works pretty much like in real life. Your feet give you the impulse to go up, but you deaccelerate because of gravity and get back down. In the game I just give the player that initial impulse. Gravity will deaccelerate him, and he will eventually stop and begin to fall, still under the effect of gravity. I don't specifically bring the player down after a jump, the game gravity does that. It looks really nice, very Sonic'ish or Mario'ish. I don't really know how do these games handle their jumping or running physics, but this way works very well.
Quote:
Yeah, you got it. You'll show when the game will be released, anyway (I hope !).
Well, I hope to see it soon! Sounds promissing! =)
blargg wrote:
I swear that in games like Castlevania, when you are standing on something it's still increasing your Y velocity (up to some maximum) so that when you walk off a platform, you fall very quickly.
That is true! Castlevania brings you down very quickly! But then again, I think the (lack of?) physics in Castlevania games (at least the older ones) suck. It feels very unrealistic for the kind of game.
I'm having some trouble understanding how to use this RLE compression methode, and yes, this is a very good one that our friend Disch told me about earlier. Observe:
map: .db $01,$00,$8C,$11.
That defines one line of tiles like with the right asm decompression code like so:
If high bit of first number is on, take the low byte, and place the following tile number to the name table that many times. If the high bit is off, then take the low byte, and write these following tile numbers to the name table as noncompressed tiles. Understand? Well, if you don't, here's a better explanation:
map:
.db $01 ;since the high bit is off, take the low byte
; and since it is 1, we leave the next 1
; hex number as an uncompressed tile #
.db $00 ;put tile #0 on the 1st tile of the name table
.db $8C ; since the high bit is on, take the low byte
; and since it is C (13), we take the next hex value
; and use it as a tile #, and paste it to the name table
; 13 times
.db $11 ; put to name table 13 times.
Is that too hard to understand? I don't think so. How could you decompress that? I was thinking some thing like this:
Code:
loadmap:
take the high byte of a value
if it's 8
take the low byte
(let's just say that the letter L= Low byte for this next part)
store the next value into the NT (name table) L times
ifzero:
take the high byte of a value
if it's 0
take the low byte
store the next L bytes to the NT as uncompressed tiles
Do this for all bytes
The problem is I don't know how I could just have it automaticly check every byte for that kind of thing. Don't you have to do that for each and every byte?
Oh, and I'm sorry, which one was the frame done flag again?
Celius wrote:
Oh, and I'm sorry, which one was the frame done flag again?
This is some flag YOU should set up, it doesn't exist. But did you understand the concept?
You have to declare a variable, call it whatever you want: "frame done", "waiting vblank", "draw frame"... no matter the name it means the same thing: if it is set (has a value of, say, 1) it means you finished all calculations (moved the players, checked for collisions, etc, etc) and must now wait for vblank to draw the frame.
It goes something like this:
Code:
MAIN_LOOP:
;here you check for keypresses,
;move the objects, check for
;collisions and everything else;
;Now you've finished calculating,
;and would like to update the graphics
;of the player, the playfield, etc,
;but you must wait for vblank.
;The most reliable way of detecting
;vblank is to use NMI's.
;So, you set this flag to keep you
;waiting while the NMI does not come.
LDA #1
STA FRAME_DONE
WAIT_NMI:
LDA FRAME_DONE
BNE WAIT_NMI
;When you get here the new frame
;will have been drawn, and you can
;start calculating the next one.
JMP MAIN_LOOP
NMI:
;Here should be the code that renders
;your new frame to the screen.
;Clear the flag and return from the interrupt.
LDA #0
STA FRAME_DONE
RTI
If you do it like this there is no way your player will move more than 1 pixel per frame.
EDIT: Of course you need to have NMI's enabled for this to work (bit 7 of $2000, I think). I heard the folks in here saying that NMI's are a much better way to "detect" vblank than the "traditional" way (checking bit 7 of $2002), since you could check the bit at the end of vblank (after all, you're still in vblank), and your drawing code could go over the vblank time and cause crap to show up on the screen. With NMI's you always assure you got full vblank time ahead.
Well, it's a way so see things, but not the only way.
Final Fantasy does call a subroutine that will enable NMI flag, then jump forever, while the NMI will disable it's onw flag, pull the 3 pushed value (processor status, programm counter old adress), then return from the soubroutine that looped forever before the NMI.
This way of doing things is not worse than the usual way, I thing, but still interessting.
All Konami games seems to constantly add a number to another out-of NMI, and *all* the game logic will be done in NMI. This is surely harder to do, and is overall pretty stupid (is the added number a reliable way to get random numbers ??).
Some games, such as Battletoads and NES Snake 2 seems to constantly do calcutions, and never do just waiting. I don't know how they work, trough, if the logic is done during or out of the NMI.
I think overall the best way is to have an NMI routine handle all the basic and very common graphic stuff, while all the rest of game logic would be done out of NMI. Doing everything in NMI is hard and overcomplicated, and doing everything out of NMI will waste a lot of ROM to do very common PPU stuff.
Bregalad wrote:
Some games, such as Battletoads and NES Snake 2 seems to constantly do calcutions, and never do just waiting. I don't know how they work, trough, if the logic is done during or out of the NMI.
I'm working on a project where I never stop calculating... But I keep setting flags to tell what I can copy when the NMI comes. This game is drawn in "columns", and I keep a counter that tells how many columns I have ready for drawing. The NMI routine draws them and decrements the counter. But I nevert stop calculating columns. I don't think this could work for a plataformer though...
Quote:
I think overall the best way is to have an NMI routine handle all the basic and very common graphic stuff, while all the rest of game logic would be done out of NMI. Doing everything in NMI is hard and overcomplicated, and doing everything out of NMI will waste a lot of ROM to do very common PPU stuff.
I plan on doing like I said in my platformer, but of course it is not the only way. The advantage is, if you by any chance go over the usual calculation time (a very complex frame, maybe), the game will slow down a little, like in Megaman for example, but you'll never risk drawing to the PPU at the wrong time.
Yeah, I'll use a method very close to your myself. I used the FF one earlier, but it overall shucked when precision for PPU writes were needed (no PPU string buffer !) -> waste of bytes, slower and harder to programm
I can just not know what the game can't stop calculating, exept maybe random number or a $4011 wave (like Battletoads... exept this ones also don't stop calculating even if there is nothing played in $4011). It is just senseless to me.
Thanks. Any ideas about loading the map data???
Design the data as you like, I recoomend not using any compression the first time, then, you can found some compression algorithms on the net or make your own, depending of how the data is organized.
The usual way is to beak a map into blocks, and each blocks will have it's own index to tile data, attributes, collision and stuff. You can do as much "block data levels" as you like. You can also do blocks that will point to other smaller blocks (I do this in my game).
To set it on the screen, turn it off then write all NamTables, and palettes to eventually turn it on later.
Bregalad wrote:
To set it on the screen, turn it off then write all NamTables, and palettes to eventually turn it on later.
Just a little question here: do you *NEED* to turn the screen off to write to the PPU during vblank? Or is it just a safety thing? I'd bet it is just to be safe...
Bregalad is right Celius. You shouldn't be worried about RLE or any compression just yet. And since you're working on a pacman kind of game, the maps are not very large and don't occupy much space, so you don't need compression anyway.
I think you should draw your maps directly in ROM, using .db statements. Arrange your map in a 2-D array (32x30, for example). So after your player just moved, and you want to check for a collision against a specific block (at position XPos, YPos), just use this formula to read the block from ROM:
(base map address) + (YPos * 32) + XPos
This will give you the right address of the block you want to ckeck. Once you loaded it, you can tell if it is solid or empty, and decide if the player can go through or not.
Okay, but is there a way to say that if your player's x and y pos reach this tile number (I'm referring to in the pattern table, not name table), collide? I'm kind of getting an idea how to do this, but not really. Please explain if there's any way to do that.
Celius wrote:
Okay, but is there a way to say that if your player's x and y pos reach this tile number (I'm referring to in the pattern table, not name table), collide? I'm kind of getting an idea how to do this, but not really. Please explain if there's any way to do that.
Well, you might just make up a "code" like this: tiles from 0 to 127 are "empty" and tiles from 128 to 255 are "solid". If your game does not use many graphics, this works well.
Just load the tile number from your map, as I said before. If it is in the range 128->255 (bit 7 set) you don't let the player go by. There is your collision.
Just be carefull about one thing: your player coordinates have pixel-accuracy, so you must get rid of that when checking the background, wich has only tile-accuracy. So, you must divide the player coordinates by 8 (shift right 3 times) to get the tile coordinates. Also, you must account for your player's size when checking for collisions to the right and bottom. But of course, this can vary depending on the way you handle your player's coordinates (I'm assuming the player coords refer to the top left corner of it).
Well, I'm just trying to figure out a way to make a semi-small routine that will automaticly check for collision. Is there any way that you can have the routine automaticly check every byte to see if bit 7 is set, for every map? not like, oh yeah, just check each individual tile on the Name Table for collision, I don't want to do that. I wish you could just say this:
collide:
load 1 byte, starting with the first one
if bit 7 is set
collide
load 1 byte, starting with the first one
if bit 7 is not set
don't collide
go to next byte
loop this routine for a long long long time.
Do you know what I mean? instead of repetative asm that is tedious and ends up causing problems, just have one nice little routine. Is this possible?
Celius wrote:
Well, I'm just trying to figure out a way to make a semi-small routine that will automaticly check for collision. Is there any way that you can have the routine automaticly check every byte to see if bit 7 is set, for every map?
Why would you want to check the whole map? You only need to check the tiles close to the player, since those are the only tiles the player can collide with. If the player is at the bottom of the screen, for example, there is no way he can collide with a block at the top of the screen!
Look at the pseudo-code example I posted a while ago. You move the player, still not knowing if this movement is allowed or not. Then you check if the player "entered" a wall. Now, if your player was moving right, you only need to check the tiles to his right! If you are moving to one direction, there is no way you'll collide in the other! See the beauty of it?!
In the example I posted, I only checked for the tiles where the player was. That's what you have to do. Only check the surroundings of the player. If you moved right, check the tiles to the right. If they are empty (no bit 7), let it be. Otherwise, move the player back. This will cause the effect of collision.
Look, I can't give you'exact information since I don't know the size of your player or the size of your tiles, etc. But tipically you'll only need to check a few tiles in the direction you are moving. Depending on the size of the player, something around 3 checks is enough. If you check the whole map, It will be terribly slow, and there is absolutelly no reason for this.
Oh, so something like this?:
Code:
rightkey:
jsr check_collision
lda rtcol
cmp #1
beq collide
dec sprx
rts
collide:
rts
check_collision:
load tile (x+1), the tile next to the one you're over
see if bit 7 is on
bne no_col
lda #1
sta rtcol
rts
no_col:
rts
Okay, I think that would be okay, I'd just need a different way to say load tile (x+1), and see if bit seven is on. By the way, this is a very newbie question, but how do you see if certain bits are on? not just comparing like this:
cmp #$80
I mean like
cmp #%10000000
but without the next 7 bits after number 7. I know that's probably confusing, but you know what I mean. I just want to see if bit seven is on. Not if the first four bits are exactly as I write them, JUST bit 7. Any way to do that? and how would I do the tile (x+1) thing? Sorry...
I'll check the rest of your post in a while I'm a bit busy right now!
But the bit thing, I suggested bit 7 because it is the easiest to check for. Just use BMI or BPL, wich will branch on bit 7 set/clear.
But for the other bits, there are many ways to test them. You could for instance AND the number with your mask, and if the result is not zero, the bit is set. I believe BIT does the same thing, but doesn't destroy the accumulator.
Okay, so how exactly would you say I see if bit 7 is on? only bit 7? I don't care about the other bits, I only care about bit 7. Is there any way I could say something like this?:
cmp #%1 ;don't care if any other bits are on, just bit 7
If I tried that, It would just say like:
cmp #$01
I can't really explain why, but I know why, It's just hard to explain. So yeah, what ways could that be said? It would really help...
You mean that the whole 8-bit argument is a waste to check only one bit, right ? If so, yeah, some processors have the ability to check a bit like this. The SPC-700 and CMOS 6502 can, I think. But the NMOS 6502 isn't able to do this. Instead, just load the tile value in the accumulator, and check for the minus flag. If a value above $80-$ff, it will be set (bmi will branch) else, it will be clear (bpl will branch).
It's just like the vait for VBlank at reset is typically done :
Code:
lda $2002
bpl -
lda $2002
bpl - ;Check twice for bit 7 of $2002
In that case the code would look like :
Code:
lda #<MapAdress
sta PointerL
lda #>MapAdress
sta PointerH
lda PlayerHPos
lsr A
lsr A
lsr A
sta Temp ;Get HPos/8 in temp
lda PlayerVPos
and #$f8 ;The value is already multiplied by 8
asl A ;By 16 now
rol Temp2 ;Save MSB
asl A ;Final result multiplied by 32
rol Temp2
adc Temp
adc PointerL
sta PointerL
lda Temp2 ;Final addition to get the pointer ready
adc PointerH
sta PointerH
ldy #$00
lda [Pointer],Y
bpl _noCollision
inc ColisionFlag
_noCollision:
rts
"Bit 7" usually means the most significant bit, the one with place value $80 = 128. The bit with place value $01 = 1 is called "bit 0".
If you want to test whether bit 0 is on, use 'and #$01', or 'lda #$01 bit address'.
You don't need any CMP or anything. If you read ANY 6502 document, you'll see that the load instructions (LDA, LDX, LDY) affect the N (negative flag). The negative bit is bit 7 (if this is set, the number can be interpreted as negative if you want). When you load a value, whatever is in bit 7 is copied to the N flag. then you just use BMI or BPL wich branch depending on the contents of the N flag. Like this:
Code:
LDA SOMEWHERE ;Loading the map tile.
BPL BIT7CLEAR ;Skip collision if bit 7 clear.
;If you're still here, there is a collision.
;So you should move the player back.
BIT7CLEAR:
;If you get here there is no collision.
JMP MAINLOOP ;Just loop back.
Of course the LDA part won't be direct like this. When reading your map, you'll most likely use (Indirect),Y addressing. So, if your map is 32x30, like I said before, you can do it like this:
Code:
;Load player Y coordinate.
LDA PlayY
;Divide by 8.
LSR
LSR
LSR
;Now, I have to explain what I'll do here.
;We must multiply the value in the accumulator
;in order to get the offset of the row the player
;is at. And we have to add that value to the base
;address of the map (address of the start of the map).
;But in this case, it is more efficient to add before
;multiplying, so I divide the base address of the map
;(in the example, $C000) by 32 (wich results in $600),
;so it will restore it's value when I multiply
;everything by 32.
;In this case we don't even need to add, since the
;lower byte of the address is 0.
STA MAP_ADD+0
LDA #$06
STA MAP_ADD+1
;Now, multiply by 32.
ASL MAP_ADD+0 ;x 2
ROL MAP_ADD+1
ASL MAP_ADD+0 ;x 4
ROL MAP_ADD+1
ASL MAP_ADD+0 ;x 8
ROL MAP_ADD+1
ASL MAP_ADD+0 ;x 16
ROL MAP_ADD+1
ASL MAP_ADD+0 ;x 32
ROL MAP_ADD+1
;Now, load PlayX.
LDA PlayX
;Divide by 8.
LSR
LSR
LSR
;Put it into Y.
TAY
;Finally, load the byte from the map.
LDA (MAP_ADD), Y
Wow, that took a while. I hope you understand. This is the simplest way I can think of reading a byte from a map.
Your map must be define like this:
Code:
.org $C000
.db $00, $00, $00, $00, $86, $86, ... (complete the 32 bytes of the row)
.db $A0, $00, $00, $00, $86, $86, ... (complete the 32 bytes of the row)
.db $A0, $00, $00, $00, $86, $86, ... (complete the 32 bytes of the row)
.db $B0, $00, $00, $00, $86, $86, ... (complete the 32 bytes of the row)
.db $B0, $00, $00, $00, $86, $86, ... (complete the 32 bytes of the row)
.
.
.
(keep until you complete the 30 rows)
Now, Celius, I'm almost giving it all ready to to you, all you have to do from here is to account for the direction the player is moving. I mean, check the tiles to the right if the player is moving right, the one at the top if he is moving up. You just have to manipulate the making of the address I discussed above accordingly. Add 1 or 2 (of course it depends on your player's size) to the coordinates we are checking.
Good luck!
Oh my... I have no idea why I didn't think of that. I must find out why I didn't think of that, because my wonderful Science teacher gave learning advice: When you wonder why you didn't think of something, figure out why, and you'll never have that problem again. Okay, anyways, I think this is what you mean by checking bit 7:
Code:
lda tilenumber
cmp #$80
bpl collide
rts
Maybe not exactly like that, but is that the general idea of what you were saying? Okay, well I could encorperate that in my code somehow. Thank you for telling me that.
Celius wrote:
Code:
lda tilenumber
cmp #$80
bpl collide
rts
No, no! you don't need the "CMP #$80" part! Just load the number! The LDA command affects the negative flag directly! don't compare anything, just load the byte!
Code:
lda tilenumber
bpl dontcollide ;You don't want to collide on bit 7 clear, remember?
;now collide
This will do the trick.
Celius, do you understand how the instructions on the 6502 affect the flags? You don't always have to do a "CMP" to make a decision. The other instructions affect the flags too. The 6502 knows a number is negative (bit 7 set) just by loading the value. No comparision is needed.
Why don't you play with a 6502 simulator, to test this little things? Just test and you'll see what I'm telling you is the truth. Just load, don't compare. In this case, of course.
Okay, sorry, when you posted, I was still posting, I need to ask you something when I get back from doing something. So just hang on a few minutes.
Okay, I understand the basic concept, but I'm a bit confused about the formula to figure out what tile your sprite is over. So could someone explain that to me again? I remember I think tepples saying:
tilex = (xpos+Xscroll)/8
tiley = (xpos+Yscroll)/8
Huh? why would that be the formula? Can I have a more newbie friendly explanation? I'm not implying that I'm a newbie, I'm just asking for a simpler explanation.
Seriously, draw a picture. Those always help me. Let the formula appear in the picture, rather than trying to find it (since you'd be trying to find something you don't yet understand). The point is to make conditions as favorable as possible for the understanding to come to you naturally. Often it doesn't come to me until I draw a picture and explore it.
You have a table arranged like this (I put random characters in it) :
A X U W O L T
S Z W K Y M Q
S U E U W P E
Q S G E I S L
Now you want to got the P that is in the third row and the sixth column.
You have two indexes, one is 2 (the count starts from zero, so the third row is number 2), and 5 (also start from zero).
The problem is that all the table is stored lineary in your ROM, so there it would be :
A X U W O L T S Z W K Y M Q S U E U W P E Q S G E I S L
The formula to know witch letter is the one you're looking for is :
There is 7 rows so, multiply the row index by 7. Scince the last letter of row 0 is at column 6, the first letter of the second row will be just after it, so effectively it's number 7.
Then, just add the column index to get the final index number.
In that case, 2*7=14; 14+5=19
Looking in the list above, the 20th letter is effectively P. It's number 19, but it's the 20th because the index starts from zero.
blargg is right. Draw it down on paper. It alway helps me too. Draw the grid of tiles, draw your player over it in different positions and try to find your formula from that. We can't give you the exact formulas, we don't know your game. We don't know the size of your map, the size of your sprites, any of that. We can only help you with the concepts, give you examples and all, but it is up to you to convert to your specific needs.
Oh, that is stupid that when bit 7 is set, the N flag is set. that's confusing, so you have to bmi for when bit 7 is set, and not bpl. I hate how everything in 6502 is like backwards. Okay, I don't mind it that much. But I'm just wondering if it's possible to load the data from a .nam file, because it still holds bytes, it's just a binary file. I think it's funny that you could actually just make a blank file, open it up in hexacute, and change the bytes, and make it into a .nam file, and it would actually show stuff. So could you do me a favor? I'm sorry, there's something I'm still not getting. I know you have to divide x and y coords by 8, but I was still confused about how to make the sprites coords and the map have anything to do with eachother. I could just say this:
collision:
do the whole lsr a thing again with cy
if tile above or below has bit 7 set ; this is the confusing thing
if not go to thing
otherwise collide
do the whole lsr a thing again with cx ; or would it be asl?
same collide routine
thing:
rts
Yeah, I'm just confused about how to say "if the tile above or below". Not even if bit seven is set, I don't know how to compare such a thing as tiles and sprite cords. There, that's it. I don't know how to compare tiles and sprite coords. Sorry, any other examples?
OK, Celius. We told you could find the formulas yourself if you drew the stuff down. But you don't seem to want that. So I'll draw it for you, only because I like to talk about this subject.
But don't just forget everything I told you before! You'll have to use this together with what I told you in past posts.
EDIT: Before we begin, I'd like you to remember how we do things: always move the player, even if there is a wall. You check for the collision AFTER you moved the player. And if there was any collision, we CORRECT the player position, so that it looks like the wall stoped him (wich is what the person playing the game sees).
Here is the notation i'll be using:
And this is what we might have at the top left corner of our game screen, at a given time:
The player is just standing there. The red point represents the player's coordinates. Now, let's do some math. From this drawing, the players coordinates are something close to (19, 11). Your background tile size is 8x8, so we divide these coordinates by 8. The result will be (2, 1) - we totally disregard fractions. Those are the exact coordinates of the block where that point is! That is enough to check for collisions. Now that you have the block coordinates, you do the math I showed you before (past posts) and when you check your map, you'll see the tile is clear.
There is only one problem. Lets try to move the player to the left:
The new coordinates are something close to (11, 11). When we divide by 8, we get the map coords (1, 1), where there is no solid block. Wich would mean the player movement was successful, but this is wrong. The player just went through a wall!!!
What do we do? Add more collision points:
Now, after moving the player, just check all 3 points as I described earlier. If all 3 points are clear, there was no collision, but if any of them are solid, there is no need to keep checking, you know there was a collision.
Since this was a collision to the left, we push the player back to the right, placing it as close to the solid tile as we can. Just set the sprite's X position to the X position of the tile you hit * 8 + 8.
In this case, the tile hit was at (1, 2). Take the X coord (1), multiply by 8 and add 8. You'll get 16, wich is the correct place for the player to be.
Seems pretty simple now, doesn't it? Just do it for all directions. Check the appropriate points for each direction you move.
Keep in mind that in this example, I used 3 points per face. This is due to the size of the player and the size of the blocks. But the general rule is: The points can't be more spaced than the block size, or you might miss a block. Like this:
Another VERY important rule is to never move the player more than the size of a block. If the blocks are 8x8, the most the player can walk in one frame is 8 pixels. If you do more, you may also miss a block.
And here are other player sizes (and their collision points) we could have in a game with 8x8 blocks:
The second example shows that you may even occupy only part of the tiles you're using, just by rearranging the collision points.
This method is very good, I've been using it for a while now. I hope you understood it better now. If not, please read carefully until you do. This is all you need for collision against the background. Collision between objects is a whole other story. You'll need to use the concept of bounding boxes, and see if the boxes around different objects overlap. If they do, there was a collision. In a game, you'll tipically loop through your objects, checking if they collided with the player, since collision between enemies or items isn't much used, only to the player.
There is one more thing I have to tell you.
The method I described above, is good for games where the player has a lot of freedom: a platformer, an adventure, etc. But you said before you were doing a pac-man styled game. Maybe, in this case, you may want to choose a simpler method of collision.
Just move the player a full block at a time. This way the collision will never happen at the middle of a block, and you just need to check (still in the suposition of moving to the left) the two exact spaces to the left of the player, before he moves.
"8 pixels at a time? You're crazy, that will look terrible!" you might say. But, what if logically you move it 8 pixels at a time, but do a little animation, instead of just skiping a full tile? Something like this: The player pressed up. You check the 2 tiles that are above the player: one is at (PlayX div 8, PlayY div 8 - 1) and the other is at (PlayX div 8 + 1, PlayY div 8 - 1), as you like formulas so much. But there may be better ways of doing this, such as keeping the player coordinates in the same scale as the background ones, and scaling up (times 8) only for displaying. If the tiles above are clear, start a little counter with the value 8. So, in the next 8 frames, you subtract 1 from the player's Y position every frame. This will give the impression of smooth movement.
Most RPG's do it like this, I think. You should do what feels most confortable and seems right for the game. The first method I described is much more verstile as it can be used for almost any game. The second method is very limited (you can't walk diagonally and can't have any acceleration, for example), and only a few types of games look OK with it, but it is much simpler to implement and will run a little faster. Not that the other method is specially slow. It is only so if you use it with many objects, but you most likely only want it for the player, and for other (simpler) objects you can use the second method.
But I still don't get how to compare tile numbers with sprite coords. I really don't mean to frustrate you, because it's just as frustrating to me. So you have to divide sprx and y by 8, so you can get it in line with tiles. but I don't know how you tell the code what tile you're over. I know how to figure out which tile the character's over, but I don't know how to tell the code what tile the character's over. Do you know what I mean? I really would appriciate if I could say this:
anotherlable:
lda xpos
lsr a
lsr a
lsr a
sta stuff
somelable:
load (stuff+1)
if stuff+1 is over a tile that has bit 7 set
then collide
I just don't know how to say that second part of somelable. Do you know how? or could you explain it to me? I'm sorry, I just don't really understand this concept, and right now, I don't feel like bieng too shy about retarded questions. Sorry. Thanks.
But I explained it already, with code and all! Here it is:
http://nesdev.com/bbs/viewtopic.php?p=4429#4429
The second piece of code has everything you need to check a point in the map. (PlayX, PlayY) is the point you want to check. You just have to repeat that piece of code for every point you want to check (the 3 points I showed you with pictures).
You must understand how a map works. A map is not a magic .NAM file that you .incbin in your code! There is no magic. Nothing can distinguish a map from a music or a porn movie, for example, except you. The program YOU code. A map is just a series of bytes, as any other piece of data or code that has ever existed. What tells them all apart is how the code you write handles that data.
Memory is linear. Address $0006 comes after $0005 and it goes like that until the end. But the map is 2-dimensional. How can you represent a 2-D map in a 1-D space? You just place row after row linearly.
Let's think decimal for a while, OK? Your map starts at address 0 (don't ever do that on the NES, I'm just abstracting here). So, address 0 contains the first tile, first column and the first row (coordinates 0, 0). The next address (1) contains the tile in the second column, first row (coordinates 1, 0) and so on, until the end of the first row.
Now, the next tile is in the second row, the one below. But memory does not know the concept of "below". In memory this is all just one BIG row. If you're following, the last tile in the first row is at address 31. What is the next address? 32! So, the first tile of the second row is at address 32.
That's why we have to multiply our Y index to the map by 32. Each row is composed of 32 bytes, so in order to get something from the 3rd row, we must first skip the first 2, wich take 64 bytes. The 3rd row is index 2 (remember, the 1st one is 0), so we multiply 2 by 32 and we get 64. That's exactly how many bytes we must skip to read something from the 3rd row. Now, we use the X coord as an index, that tells how far in this row is the byte we want.
That's what the code I posted before does. At first, I didn't wan't to give it all to you. But in the end I did. With the stuff other people and I posted in this thread, you can make a very decent collision detection system, it is all here, and I literally mean ALL. Take a good read at the complete thread, from the beginning. The ideas, concepts and even the code is all here. You just have to adapt them to your specific situation.
I fail to see what exactly you don't understand... and you seem to be just ignoring what we say. I write a huge-ass message, explaining it bit by bit, and you come with the same question from 10 messages ago. You are not paying attention, and that's really frustrating, 'cause I really would like you to understand. No one else is replying to this thread anymore, except the 2 of us. Maybe it is because you don't pay attention to what people say.
And if you don't know how a 2-D array is laid out in memory, maybe you shouldn't be programming a game yet. Sometimes it seems as you lack the knowledge of many basic principles needed in game programming. And starting at such a low level as 6502 assembly might not be good.
Note that I'm not saying you don't know asembly, but things that are accomplished easily in high level languages are quite difficult to implement in assembly. and if you don't know them in a higher level, it will be really hard to understand it in low level. I don't mean the language, but the data structures and other logic wich make much more sense in higher levels.
For God's sake, the collision you're having so much trouble with can be done in ONE line of code in higher level languages:
Code:
if (Map[pointX div 8, pointY div 8]) > 127 then playX := pointX and $F8 + 8;
This single line of Pascal code will check if a collision to the left happened at point (pointX, pointY) and put the player back to the right if it did.
And I think it is much simpler to understand the concept like this than in assembly. Maybe you should experiment more, read more. There is a qbasic site, I think, with great links to game programming tutorial, not related with the NES at all, but cover many of the concepts needed in game programming. Maybe you should google for it, I think it is "Pete's QB site" or something.
Sorry if I seem angry or something, I'm just a bit frustrated. I would still like to help you out, but only after you're past this. There is nothing more for me to say about collision, really. When you understand collision, and I believe you can, with all the material in this thread, we can gladly discuss the next problem.
I'm really really sorry, I didn't mean to be a pain. I read through this forum, and I understood a little more, and I really didn't mean to ignore everyone, there was just some things I didn't understand, and I guess I wasn't asking about them clearly enough. Well anyway, I used your example, and guess what! it works! Except I just have two problems. Are you willing to help me just a little more? Please? I would deeply appreciate it. I just have two problems. One is, that the collision areas act as collision columns, so I collide in the columns that the tiles are in. and also, I can't think of a way to make it so it's like when you're on the right side of the object, you can still move right, up and down, but not left. I have my code set up like this:
Code:
collision:
;the collision stuff which I already understand pretty much
lda (map_add), y
sta stuff
lda stuff
bpl nocol
lda #$01
sta lfcol
;I would have it storing 1 in rtcol, upcol, and dncol, but that would mean I can't move anywhere once there's a collision.
nocol:
lda #$00
sta lfcol
sta rtcol
sta upcol
sta dncol
If you haven't noticed lfcol, rtcol, upcol, and dncol are variables for left collision, right collision, upper collision, and lower collision. I'm not really sure how to fix this. I really hope you'd consider helping me just a little more, because I finally understand the main part. And once again, even if you don't help me, I'm sorry that it took so long for me to understand. I would deeply appreciate it if you just helped me a little more, and then I'd be done with this, and then we could move on. I'll actually read it many times and try to understand. Please help me a little more. Thanks if you do.
I don't mind helping at all if you actually pay attention and figure at least some things out.
There is one thing I'd like to say about one piece of code, though:
Code:
lda (map_add), y
sta stuff
lda stuff
bpl nocol
There is absolutelly no need to store the byte in "stuff". After you did "lda (map_add), y" the N flag was already altered by the loaded value, so there is no need to store it and load it again. Change to this and it will run just fine:
Code:
lda (map_add), y
bpl nocol
This is obviously faster. Not that speed is much of a problem for now, but avoiding unnecessary operations can only be a good thing, especially when you work on bigger projects where speed is really important.
Quote:
One is, that the collision areas act as collision columns, so I collide in the columns that the tiles are in.
I didn't understand this. Maybe you could take a screenshot or something, or explain better what's happening. What are you calling "collision areas"? A solid block? the collision points in the sprite? I didn't understand.
Quote:
and also, I can't think of a way to make it so it's like when you're on the right side of the object, you can still move right, up and down, but not left.
You just have to repeat the code you used for left collision with the other points: 3 in the top, 3 in the right and 3 in the bottom.
I suppose that the player coordinates refer to the top left as in my past examples. So, when you move to the left, there are 3 points to check for collision:
1. playX, playY (the one at the top left of the sprite)
2. playX, playY + 8 (the one in the middle, still in the left)
3. playX, playY + 15 (the bottom one, in the left)
These were the coordinates of the 3 points you have to check for collisions to the left. If you think a bit, the tree points to check for collisions ABOVE the player are:
1. playX, playY (same as the one we use for the left)
2. playX + 8, playY (the one in the middle, still in the top)
3. playX + 15, playY (the one to the right, stil in the top)
For the RIGHT:
1. playX + 15, playY (the one at the top right corner)
2. playX + 15, playY + 8 (the one below it)
3. playX + 15, playY + 15 (the one at the bottom)
For BELOW:
1. playX, playY + 15 (the one at the bottom left corner)
2. playX + 8, playY + 15 (the one next to it)
3. playX + 15, playY + 15 (the one at the bottom right corner)
These are all the points you have to check for each direction the player moves. No need to chack them all at once! Only check for right collisions if the player moved right. There is no way you'll collide in the left when moving right, so there is no need to check it. You'll at most check 2 collisions, if the player can move diagonally, wich means it can move in both axis at once.
I don't think you need lfcol, rtcol, upcol and dncol at all. The only thing you have to do after a collision is move the player back. You don't need to create a flag for that, just move the player back right when you detect the collision.
I suggest you do it like this:
Code:
moved_left:
;Set the first point.
LDA playX
STA pointX
LDA playY
STA pointY
;Check the first point.
JSR check_collision
;Jump if collision happened.
BMI left_collision
;Set the second point.
LDA playY
CLC
ADC #$08
STA pointY
;Check the second point
JSR check_collision
;Jump if collision happened.
BMI left_collision
;Set the third point.
LDA playY
CLC
ADC #$0F
STA pointY
;Check the third point
JSR check_collision
;Jump if collision happened.
BMI left_collision
;If you get here, no collision
;happened, so, skip the position
;correction step.
JMP end_left_collision
left_collision:
;Here we fix the player's Y position.
LDA playY
;Clear the lower 3 bits
AND #$F8
;Add 8 to place it to the right of the solid tile.
CLC
ADC #$08
STA playY
end_left_collision:
;If you make the checking of each direction
;as subroutines, you can just return now.
RTS
check_collision:
;do all that collision crap I
;explained befor in here. But
;use "pointX" and "pointY"
;instead, so you have a generic
;function that can check any
;points you want it to.
.
.
.
LDA (map_add), Y
;OK, if it was a solid block,
;the N flag is set.
;Return from the routine, having
;the N flag to indicate a collision.
RTS
That's it. Just do it to the other 3 directions (up, right down). I'll show you ONE more collision, but you have to find out how to do the rest of them!
Code:
moved_down:
;Set the first point.
LDA playX
STA pointX
LDA playY
CLC
ADC #$0F
STA pointY
;Check the first point.
JSR check_collision
;Jump if collision happened.
BMI bottom_collision
;Set the second point.
LDA playX
CLC
ADC #$08
STA pointX
;Check the second point
JSR check_collision
;Jump if collision happened.
BMI bottom_collision
;Set the third point.
LDA playX
CLC
ADC #$0F
STA pointX
;Check the third point
JSR check_collision
;Jump if collision happened.
BMI bottom_collision
;If you get here, no collision
;happened, so, skip the position
;correction step.
JMP skip_bottom_collision
bottom_collision:
;Here we fix the player's X position.
LDA playX
;Just clearing the 3 bits will
;do the trick this time.
AND #$F8
STA playX
skip_bottom_collision:
RTS
These functions are just clones of each other, with minor changes. you have to set the 3 correct points (the ones I listed before), and correct the position of the player accordingly. The 2 routines I posted should be more than enough for you to figure out the rest.
Now, I can't say this code will work for sure, as I have not tested it, and I just typed it all down. There may be errors. So don't just go and copy it and paste into your game. Read it carefully and try to understand what I'm doing here.
EDIT: Just to make it clear: The code I posted here should be run AFTER you moved the player. I'm assuming you already read the joypads AND already incremented the player coordinates accordingly. In fact, it wold be even better, in my opinion, if you include the "coord increment" step INSIDE the function that checks for collision (the ones I just posted). Just do it in the very beginning of each function. I see no reason to do it OUTSIDE the functions if you're going to jump to them right after updating the coords.
I believe you are explaining this with a 16x16 sprite in mind, which is okay, because I am going to use a 16x16 sprite in a game that I am going to make in the future, but right now, I'm starting out simple with a crappy looking 8x8 sprite. That is what you were explaining right? okay. And by the way, I don't really copy/paste too much, I look at the code and type it in, making modifications that will fit my code as I go. And I just realized my map was loaded incorrectly. I don't know why, I load it like a normal map. Like, you know how you load a text message, like for instance, .db "hello world" would be loaded like this:
ldx #0
ldy #11
loadtext:
lda text, x
sta $2007
inx
dey
bne loadtext
yeah, I load the map at $C000 like that, and it screws up, and doesn't work at all. I have it just loading 255 in y, and doing that method. And I have exactly 255 values in the map, and it loads incorrectly. There's a bunch of mishmash on the NT. Sorry, I don't mean to have the main problem that this thread is discussing branch out a thousand times into other dumb problems. But this is probably a leading cause of the collision problem. any suggestions?
AHHHHHHHHH!!!!!!!!!!!!!!!!!! THIS IS SO STUPID!!!!!!!!!!! Okay, I know that no body wants to talk about his anymore ever again, but I was just wondering what is wrong with my code. Okay, I tried to make my own collision code, and I cannot figure out what was wrong! I need your help. Okay, here is my code:
Code:
collision:
lda #HIGH(bg1)
sta turd
lda #LOW(bg1)
sta turd1
lda cy
lsr a
lsr a
lsr a
sta tiley
lda cx
lsr a
lsr a
lsr a
sta tilex
lda tiley
asl a
asl a
asl a
asl a
asl a
sta vara
lda tilex
adc vara
sta turd1
ldy #$00
lda (turd),y
bpl nocol
lda #$01
sta colflag
rts
nocol:
lda #$00
sta colflag
rts
Do you see anything there that is easily spotted wrong? And I tried using code that you guys supplied, that didn't work. I'm angry! What happens is it is constantly making my player collide. Even when I'm on Tile #$00. What's going on here? How can I fix this? Please, help, and be kind. Thanks.
-Celius
I just saw two problems.
The first one :
Code:
lda #HIGH(bg1)
sta turd
lda #LOW(bg1)
sta turd1
I don't know what you call turd1, but if it is turd plus one, then it's wrong because 6502 opears in little endian mode, meaning that the low byte should be befor the high byte. But if you make it the way that turd1 is one byte befor turd, it's allright. In that case :
Code:
lda (turd),y
Should be replaced by "lda (turd-1),y"
The other problem comes when you're adding tilex to 32*tiley, you store the result in var1, and you don't load your adress proprely. I mean :
If the result of this addition is only 8-bit, then just load Y with the result of the addition (this is probably not the case here).
If the result is 9-bit, you should increase the high pointer after the addition if the carry is set. Then, load Y with the result would be simpler, but if you want to do it the way you're doing there, you should add the content of the low pointer to the result, then once again increment the high pointer under a carry condition.
I'm a little confused on how to do 16 bit stuff, since you can only have 8 bits in the accumulator. I know you can do stuff with the carry flag, but I don't know how. Okay, I'm confused about how to like load 16 bits. I was looking at your code you supplied eariler bregalad, and I don't understand how you just randomly had the variable "pointer" and "temp2" just randomly get values. Like you didn't store anything in temp2 when you ROLd it, and you loaded pointer without even storing anything in it, did you mean pointerH? I'm sorry, I'm really confused, and i'm angry about this and my EPROMs that are all stupid. Please explain.
To add two 16-bit numbers:
Code:
clc
; add the low order bytes
lda num1
adc num2
sta sum
; add the high order bytes
lda num1+1
adc num2+1
sta sum+1
You often see stuff like this in games' physics code, as they use the low byte to represent subpixel position.
Say variables turd = $80 and turd1 = $AE
Okay, so like this:
Code:
ldy #$00
lda (turd),y
since turd1 is right after turd would load $80AE? Or would it load $AE80?
It'd be $AE80. Just remember with 16-bit stuff that the low byte is first.
Okay, NESASM is being very weird, and it only makes the collision "work" when the code is lda [blah],y instead of lda (blah),y. here is my code:
Code:
lda #LOW(bg1)
sta turd
lda #HIGH(bg1)
sta turd1
lda itx
lsr a
lsr a
lsr a
sta tilex
lda ity
lsr a
lsr a
lsr a
sta tiley
lda tiley
asl a
asl a
asl a
asl a
asl a
sta vara
lda tilex
adc vara
sta varb
lda turd
adc varb
sta turd
ldy #$00
lda [turd],y
bpl _noCollision
lda #01
sta colflag
rts
I don't know how to say increment the high byte when the low reaches 00 again. How should I incorperate that in my code?
yeah Nesasm uses yukky brakets for indirection instead of parenthesis... one of my annoyances with it.
anyway... if ADC addition causes a wrap (FF->00), the C flag will be set... you can check the status of the C flag after addition and increment the high byte if it's set.
Code:
lda turd
adc varb
sta turd
bcc skip_inc ;if carry is clear, skip over... otherwise....
inc turd1 ; increment high byte of address
skip_inc:
ldy #$00
lda [turd],y
;etc
alternatively you can use ADC to add the carry to turd1:
Code:
lda turd
adc varb
sta turd
lda turd1
adc #$00 ;add the carry flag
sta turd1
ldy #$00
lda [turd],y
;etc
using adc avoids the use of the label, but takes 2 more bytes and is a bit slower.
Another annoyance with nesasm: no nameless labels
Some will say I'm highhacking a thread, but about NESASM anoyances, I think the two dish mentionned are effectively annoning, but the one that definitely make NESASM suck is how banks are organized. Everything (including PRG and CHR) is organized in 8kb banks, and that's EXTREMLY anoying.
You'll probably have to be familiar with those maths stuff and carry if you code things at a decent speed. I think that most tutorials does only mention things techinically (aka : ADC add operand with acumulator. Flags affected : C, N, Z), and does not explain things much how they'll work in practice (like 16-bit addition code). So people new at programming like Celius can miss some really basic stuff while learing some advanced ones, just because tutorials aren't focused on the right things.
Speaking of that...
I just noticed Celius isn't putting a CLC before his ADC (and it's after a crapload of ASLs, so it's possible C might be set).
@ Celius: If the C flag is set when you do ADC, an extra 1 gets added to your sum. Example:
Code:
SEC
LDA #$02
ADC #$02
; A is now 5, not 4
If C is potentially set by a previous instruction (like say, ASL), and you don't want that extra 1 added, you can avoid it by putting a CLC command before the ADC
My collision code goes wrong at random places! Well, I'm incbining a nam file , would that cause that? Like I have a maze, which is in my opinion, the perfect testing for collision. I have squares layed out like a maze. Well I have my code so you'll decrease your position before checking for collision, so you'll press left against a block once, and you'll move a pixel. I'm fine with that for now. But I'll be pressing down when I'm sort of stuck in a wall, and there'll be a block the the collision code will skip! and there's like a few chunks that have blank spaces, but I'm colliding against them! Why? Here is my code:
Code:
collision:
lda #LOW(bg1)
sta turd
lda #HIGH(bg1)
sta turd1
lda itx
lsr a
lsr a
lsr a
sta tilex
lda ity
lsr a
lsr a
lsr a
sta tiley
lda tiley
asl a
asl a
asl a
asl a
asl a
sta vara
clc
lda tilex
adc vara
sta varb
clc
lda turd
adc varb
sta turd
bcc noinc1
inc turd1
noinc1:
ldy #$00
lda [turd],y
bpl _noCollision
lda #01
sta colflag
rts
What is wrong?
I looked at FCEUXD ultra's debugger thing, and I noticed that the carry flag is NEVER set. Why? When a byte reaches 100, doesn't it store 00 in the byte, and 1 in the carry flag? I don't know too much about that, I suppose. I really need to know though, what the problem is.
'inc' and 'dec' do not touch the carry flag.
Oh, okay. By the way, this is random, but happy 500th post! I feel special when I've posted alot.
Okay, that still doesn't answer my question. I am having a problem. My player will collide with blank tiles, and i'm not sure why! It will skip solid tiles and read them as blank, like 4 or 5 in a row sometimes. Usually it's like 3 tiles going down, and it will skip the middle one for some reason. And it will always skip it, and it's really annoying. And my code is what I posted above, and it's really stupid that it doesn't work! Any ideas why?
Well, you really need to found such problems by yourself. Debugging can get *very* anoying sometimes, but debuging the code of someone else is even worse, so try to only post "main" problems, while you need to be able to found all small ones by yourself.
Note that this code can easily be optimized (stuff like lsr followed by asl can rather be made with and) but it won't change the functionality or you programm, only speed and size.
Also, you're using a variable (here Colflag) to return the exit parameters (in that case if collision is active or not).
For a such subroutine, you can use the carry flag as one bit of parameter, so you can made your routine in the way that the carry is clear if there is no collision, or set if you have a collision (or the other way arround). Then the routine calling this subroutine will simply check for the carry flag to know if there is a collision. The end of your code will look like this :
Code:
lda [turd],Y
asl A ;The bit 7 of accumulator transfers into C
rts ;If bit 7 was set, there is a collision, and that state is returned trough carry
You simply avoid using an useless variable, and you code is much shorter. Also, in the example you posed, you seem to write #$01 in your collflag if thereis a collision but what about if there is none ? You may forgot to paste the piece of code writing #$00 into colfag there, but who knows ?
Oh, I just figured out that ASL puts stuff in the carry flag! OOPS! I thought it just put bit 7 in the land of nothingness, and put 0 in bit 0! I have to study my code now. But, I was going to ask you a question, and I really don't want to ask it, but I have to, and I appologize for making it hard for anybody, and I will try and do these things myself. Okay, so, how are you supposed to check if the carry flag has 1 in it? Oh, like BCS? Okay, I think I get it. I am seeing that in my code, I have it so I am doing an ADC right before I say BCC, and that could be causeing problems, and C would always be set right? but then I look in the Debugger, and it's NEVER set. I don't know why, and it's making me mad. If I do AND instead of ASL, how will the carry flag ever be set? will it be? I'm thinking not, and I'm wondering what I should do. Okay, well, i am going to study my code now.
Then you really don't have to cry when someone redirect you to 6502 opcode list or something.
bcc/bcs does branch in function of the carry, but rol/ror can also get it's value into an other value and that's VERY usefull.
After a AND instruction, the carry won't be affected, so it will stay what it was before. To set the carry to a know state, just use sec/clc.
Yeah, I just realized what's wrong! It never increases the high byte of the map data! Okay, I'm thinking of how I can do this. I have an idea but I don't know how to say it. I wish there was something like "BWA" which is Branch on Wrap Around. That would be SO MUCH EASIER. When something wraps around, is the carry flag affected at all? What flag is affected at a wrap around? I could probably use this to say to increase the high byte! I have to do some thinking...
Quote:
I wish there was something like "BWA" which is Branch on Wrap Around. That would be SO MUCH EASIER. When something wraps around, is the carry flag affected at all? What flag is affected at a wrap around? I could probably use this to say to increase the high byte!
That's the exact idea. If you're using INC or DEC you can detect wrapping around by checking the Z flag. $FF + 1 = $00 (Z flag set). If you're using ADC you can just use the carry flag. If it is set, the number wraped around. There is no need for a "BWA". BEQ or BCS (depending on how you're incrementing the number) will work just fine.
Quote:
There is no need for a "BWA". BEQ or BCS (depending on how you're incrementing the number) will work just fine.
Put another way, BWA already exists, just as BLT (signed branch if less than) and BGE (signed branch if greater than or equal); they're just spelled differently in an assembler (BEQ/BCS, BCC, and BCS).
There's something wrong here, at the end of my reset routine, I did a clc, and then there was my endless loop. Okay, then I went to FCEUXD ultra's debugger, and the carry flag wasn't set, so I went, okay, and I decided to do a test. I changed every clc to an sec, and guess what, the carry flag was never set, according to fceuxd ultra's debugger! why!? I DON'T KNOW! that's really stupid! and if I deleted the clc at the end of the routine, and changed all secs to clc, it would always be set! WHAT'S THE DEAL!? This is causing alot of problems! WHY DOES THIS HAPPEN!? Thank you for helping me you guys, I really appreciate it.
Every time you perform an addition/subtraction or bit-shift instruction (ADC/ SBC/CMP/CPX/CPY, ASL/LSR/ROL/ROR), the carry flag will be modified. ADC/SBC/ROL/ROR use carry as an input AND and output, while CMP/CPX/CPY/ASL/LSR use it only as an output.
Just doing clc at the end of the reset routine is totally stupid, scince your indefinite loop won't use the carry.
Use clc/sec before an addition/substaction or while exiting of a routine using carry flag as a parameter, when you know that his state won't be modified between the setup of the carry flag and the use of it.
See what I mean :
Code:
lda Number1
clc
adc Number2
sta Number1 ;PROPRELY add two values
will work fine.
Versus :
Code:
clc
lda Blahbla
sta blahblah
and blahblah
eor blahblah ;Lots of instruction that doesn't affect the carry flag, so it will remain clear
ora blahblah
sta blahblah
tay
lda Blahblah,Y
adc Number2 ;The carry flag... should be clear here
sta Number1
But then, if you modify the code between clc and adc, you may add something modifing the carry flag, and then your adc will go wrong and randomly add 1 more, and you will be angry because you won't found the error.
In some case, like after an ASL where you know that the bit 7 was clear, or typically after a BCS, you can skip the clc before the addition, because you are sure that it's clear, but beware ! If you change your code, you have to don't forget to reconsider that optimization.
The code you posted several topics above looks fine for pointer maths, so I really can't say what's wrong.
Okay, I swear that %$#@^%#% carry flag hates me! Okay, this is the funniest thing. If I did a clc right before a bcc, the carry flag would still be set! HA! how do you explain that one? I mean, you could explain it like, since that's going on in your NMI routine, their could be something wrong in your endless loop which is traveling at speeds much faster than the NMI. BUT! there's just jmp endlessloop in my endless loop. WELL! I've at least found a temporary way around it! HA! It works! Not something you want to use for big games, though: Here it is:
Code:
.bank 0
.org $8000
.incbin "maze.nam"
...
lda #HIGH(bg1)
sta turd1
...
collision1:
lda #LOW(bg1)
sta turd
lda cx
lsr a
lsr a
lsr a
sta tilex
lda cy
lsr a
lsr a
lsr a
sta tiley
lda tiley
asl a
asl a
asl a
asl a
asl a
sta vara
clc
lda tilex
adc vara
sta varb
clc
lda turd
adc varb
sta turd
jsr increase
noinc11:
ldy #$00
lda [turd],y
bpl nocoll
lda #01
sta colflag
clc
rts
nocoll:
lda #$00
sta colflag
rts
increase:
lda tiley
cmp #$00
beq store80
cmp #$08
beq store81
cmp #$10
beq store82
cmp #$18
beq store83
rts
store81:
lda #$81
sta turd1
rts
store82:
lda #$82
sta turd1
rts
store83:
lda #$83
sta turd1
rts
store80:
lda #$80
sta turd1
rts
Yeah, I won't want to store direct values into turd1. But for a one screen/one level game, it will work fine. I need to study the carry flag some more. I swear, the carry flag does nothing I tell it to. Well, it does, but I don't remember ever telling it to do things that it does!
Celius wrote:
Okay, I swear that %$#@^%#% carry flag hates me! Okay, this is the funniest thing. If I did a clc right before a bcc, the carry flag would still be set! HA! how do you explain that one?
clc clears the carry flag, you probably have trouble assembling your code if that doesn't even work.
Optimization suggestion for 'increase' routine:
Code:
increase:
lda tiley
and #$E7 ; if any bit besides $10 or $08 is on
bne :+ ; skip over
; here, we know tiley is $00, $08, $10, or $18, no other value
lda tiley
lsr A
lsr A
lsr A ; $00 becomes $00, $08->$01, $10->$02, $18->$03
ora #$80 ; add #$80 to the deal
sta turd1
: rts
If you already know tiley is already either $00, $08, $10, or $18, you can skip over the first 3 instructions. This avoids a big ugly CMP/BEQ chain and shrinks the size of the routine. As well as elimanates the need for those ugly storexx blocks
I never would have thought of that. It's completely logical though! Okay, I need to use "and" and "ora" and "eor" way more often. I hardly ever use them, and that's not good at all. I'll make those modifications. Thanks!
Hmm, my BKG Graphics Test does simple collision. The player moves in 8-pixel increments. Whenever the player tries to move, the game checks first if the tile they're trying to go to is blocked (i.e. he can't go there) and if it is then the player won't move. But you're probably trying something more complicatted, so I'll back out.
Well, collision with 8 pixel increments can be very simple, but also very ugly. If you're learning about collision, that is the way to start. But no commercial games use it, that I'm aware of. Unless the colision is tile based, but the movement is animated, resulting in smooth animation with no extra complexity. That was often used with RPG's, and loks nice, but is worthless for a platformer.
Thexeder use that, but the games truly suck.
Bregalad wrote:
Thexeder use that, but the games truly suck.
Platformer? If yes it must really suck. I don't think I ever saw a commercial platformer working like this. Maybe on reeeeeally limited systems, way more limited than the NES, but even so...
There would be no acceleration at all... and jumping would absolutelly suck.
Hocus Pocus, a shareware platformer for PC, used essentially 8-pixel horizontal movements because of the limitations of scrolling on VGA.
Hocus Pocus? Really? I remember playing that but don't remember it having bad scrolling. Of course, I played that a loooong time ago on my 386 PC way before I had any programming knowledge whatsoever... So, yeah, it may have gone unnoticed! =)
tokumaru wrote:
Platformer? If yes it must really suck. I don't think I ever saw a commercial platformer working like this. Maybe on reeeeeally limited systems, way more limited than the NES, but even so...
There would be no acceleration at all... and jumping would absolutelly suck.
It does. Just check it out if you want. And yeah, it's a crap NROM game, but still a NES game.
A lot of MSX games has such tiled scrolling as far I know. That include the original version of Gradius, Vampire Killer (Castlevania) and many other celebrated games.