It is currently Sun Nov 18, 2018 4:00 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 36 posts ]  Go to page 1, 2, 3  Next
Author Message
 Post subject: Game entity management
PostPosted: Tue Jul 28, 2015 11:09 am 
Offline

Joined: Sun Mar 08, 2015 12:23 pm
Posts: 287
Location: Croatia
I'm trying to build my own entity framework of how game entities are initialized and their codes executed.
I'd like to know if there are already existing entity engines so I can learn from them just in case that I don't waste my time on discovering something that already exists.

I have an idea and I'm currently writing code for it:

There's a game entity pointer array and a game entity heap. The game entity pointer array contains a 16-bit pointer to the game entity which is in the game entity heap. If I only had the heap, the entity execution order would be fragmented. Let's say I have 12 entities that can be on screen at the same time.
Code:
Heap: x,x,x,x,x,x,x,x,x,x,x,x
Heap: 0,1,2,3,4,x,x,x,x,x,x,x Adding 5 entities
Heap: 0,1,x,x,4,x,x,x,x,x,x,x Removed entities 2 and 3
Heap: 0,1,5,6,4,7,x,x,x,x,x,x Added 3 new entities

According to how I see a game engine, the entities #5, #6 and #7 should run after entity #4.
Code:
Heap: 0,1,x,x,4,5,6,7,x,x,x,x
Heap: 0,1,x,x,4,5,6,7,8,9,A,B Added some more entities
Cannot add any more entities without destroying the code execution order.

So because of this, I have an array of pointers that fills like this:
Code:
Array: x,x,x,x,x,x,x,x,x,x,x,x
Array: 0,1,2,3,4,5,6,7,x,x,x,x Add 8 entities
Array: 0,x,x,x,x,x,6,7,x,x,x,x Removed some entities
Array: 0,x,x,x,x,x,6,7,8,9,A,B Trying to add more

Now begins the sorting
Code:
Array: 0,6,7,8,9,A,B,C,D,E,x,x Sorted and added more entities

Sorting by moving the giant entity structs would be ultra stupid so it's better to move the pointers.

Every entity has its data structure like this: (Note, I'm actually using ASM and not C++, but I like to write C++ code before doing ASM so that I know what I'm doing)
Code:
class instance
{
   char State
   short ID
   char* Code // (pointer to pointer to this instance's code in ROM)
   uint24_t X // Low byte is the fraction for subpixels and subsubpixels, mid byte is for moving on the screen page and high byte is for moving through multiple screens
   uint24_t Y
   char Attributes
   // Attributes: 7=visible,6=solid,5=enablecontrol,4=substitutivecontrol
   //             3:0=qualifiers{3=enemy,2=neutral,1=friend,0=player}
   char BehaviorTemplateLow
   // Behaviors:
   // 7=disablePhysics,6=topBounce,5=bottomBounce,4=horizontalBounce,
   // 3=freeUpDown,2=freeRightLeft,1=enableGravity,0=enableJump
   char BehaviorTemplateHigh
   // Behaviors:
   // 7=enablePseudo3D,6=Zfree,5=Zgravity,4=Zbounce
   // 3=Zjump,2=Zjetpack,1=ZcontrolByJump,0=ZcontrolByUp
   _tButtons ControlSubstitutive // Over here are the buttons that are pressed for the entity by artificial intelligence
   char ControlPtr // Pointer to global control that controls the object
   char SpeedX
   char SpeedY
   char AccX
   char AccY_or_Jump
   char DecX
   char DecY_or_Gravity
   char AnimAttributes
   // 7=stopped,6:5=forcedPaletteIndex,4=forceOwnPalette
   // 3=loop,2=returnToStart,1=verticalMirror,0=horizontalMirror
   char AnimNumber
   char AnimSpeedCounter
   char UserVariables[6]
   char JumpTimeCounter
   char JumpTimeMax
   char Z
   char SpeedZ // Used if pseudo3D is on
   char AccZ_or_ZJump
   char DecZ_or_ZGravity
}

The physics code works for each entity the same, but acts differently because for example a player can't move freely up and down because it's not a bird, but parakoopas can. These flags are checked and then the usual code executes like:
Code:
SpeedX+=IsBitOne(ControlSubstitutive,RightButton)*AccX
X+=SpeedX

I mean, there's tons of code, but you basically know what I mean.
Every entity has its code for execution for example for AI or colissions with powerups or each other.

What do you think? Would this work?


Top
 Profile  
 
PostPosted: Tue Jul 28, 2015 3:26 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10974
Location: Rio de Janeiro - Brazil
I honestly don't understand the need for sorting at all. I mean, game entities are created and destroyed all the time, it's not like there's any intrinsic relationship between all of them that dictates that objects spawned later should be processed later.

That being said, you can easily keep newer objects after newer ones if you use a linked list, completely eliminating the need for sorting. Keep a pointer (or an index, whatever) to the first object in the list (for processing purposes), and have each object point to the next (also for processing). Another pointer indicates the last object in the list (which has a null pointer indicating there's no next object), so you can easily insert new objects after it. Here's the empty list:

Code:
First free slot: 0
First occupied slot: x
Last occupied slot: x
Objects: x,x,x,x,x,x,x,x,x,x,x,x
   Next: 1,2,3,4,5,6,7,8,9,A,B,x


When the slot is empty, "Next" is used to point to the next empty slot, when the slot is occupied by an object, "Next" points to the next object's slot.

Now, to add 1 object, claim the first empty slot by making the one it points to the new first empty slot. The first free slot is 0, and since it points to slot 1, slot ones becomes the first free slot. Since the list of occupied slots is empty, there's no need to connect the new object to anything:

Code:
First free slot: 1
First occupied slot: 0
Last occupied slot: 0
Objects: G,x,x,x,x,x,x,x,x,x,x,x
   Next: x,2,3,4,5,6,7,8,9,A,B,x


To add another object, we claim a new slot just like last time: claim slot 1, and make slot 2 the new first empty slot. To make sure the new object is processed after all others, have the current last object point to the object we just inserted, and make the new object the last.

Code:
First free slot: 2
First occupied slot: 0
Last occupied slot: 1
Objects: G,H,x,x,x,x,x,x,x,x,x,x
   Next: 1,x,3,4,5,6,7,8,9,A,B,x


After inserting 3 more objects, we have this:

Code:
First free slot: 5
First occupied slot: 0
Last occupied slot: 4
Objects: G,H,I,J,K,x,x,x,x,x,x,x
   Next: 1,2,3,4,x,6,7,8,9,A,B,x


To remove an object, simply have the one that come before it point to the one that comes after it (to know which object come before you also need a list of "Previous" pointers). If the object being deleted is the first or the last, adjust those pointer accordingly. Finally, add the slot back into the list of free slots, by making it the first, and having it point to the old first. Here's the list after object J is removed:

Code:
First free slot: 3
First occupied slot: 0
Last occupied slot: 4
Objects: G,H,I,x,K,x,x,x,x,x,x,x
   Next: 1,2,4,5,x,6,7,8,9,A,B,x


That's about it. To process objects, just start with the slot pointed identified as the first and follow the pointers.


Top
 Profile  
 
PostPosted: Tue Jul 28, 2015 5:23 pm 
Offline

Joined: Sun Mar 08, 2015 12:23 pm
Posts: 287
Location: Croatia
How about this? There's a pointer to the first entity to be executed and a pointer to the first entity to be shown. Every entity has a pointer to previous and next entity to execute and render. If I want to set one entity before another in rendering, I just swap the pointers.


Top
 Profile  
 
PostPosted: Tue Jul 28, 2015 8:10 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10974
Location: Rio de Janeiro - Brazil
Why do you need different processing and rendering orders?


Top
 Profile  
 
PostPosted: Tue Jul 28, 2015 9:50 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6956
Location: Canada
tokumaru wrote:
Why do you need different processing and rendering orders?

Order of update can have a big impact on how objects interact with each other. Could be very important to have specific orders, esp. with things like moving platforms.


Top
 Profile  
 
PostPosted: Wed Jul 29, 2015 5:54 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10974
Location: Rio de Janeiro - Brazil
That I can understand, although I usually try to avoid interactions between objects that are not the player.

I think that maintaining 3 linked lists at the same time (two of which are composed by the same slots) a bit of overkill. In order to process objects in a constant order I would probably use the linked list for that, and have them set a metasprite pointer in their RAM slot, along with a priority flag. Then, once all objects have been processed, I'd run through them again in pseudo-random order rendering the sprites, to the beginning or the end of the OAM depending on the priority flag.


Top
 Profile  
 
PostPosted: Wed Jul 29, 2015 10:21 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 20783
Location: NE Indiana, USA (NTSC)
tokumaru wrote:
Why do you need different processing and rendering orders?

A game with a semi-overhead view may want to sort objects' rendering orders.


Top
 Profile  
 
PostPosted: Wed Jul 29, 2015 3:56 pm 
Offline

Joined: Sun Mar 08, 2015 12:23 pm
Posts: 287
Location: Croatia
I was thinking that in my Mario clone game all objects should interact with each other like Goombas colliding with each other or accidentally stomping each other or have one turtle shell roll and touch a moving platform which bounces it off or lifts it up.
I keep forgetting that NES is very weak so I don't know will it even be able to handle that stuff. I estimate to have the maximum of 16 active entities on screen at the same time. What do you think? What does SMB3 have for example? Can you tell me these parameters of other games?

About sprites, I think that the player sprites should almost always be above everything. But what if you have an animated door object? It should first be behind the player when it's opening and then in front of player when it's closing. Also, when the player enters a pipe, a new sprite that's behind the background and the same size of the player sprite should appear on the pipe so that the player actually disappears behind the pipe, but not behind the background (for example bushes near by). Also, if there's an explosion or a cloudy poof effect like in SMB3, it should definitely be in front of everything.

I was going to ask should I use double linked lists or single linked lists. And is there a code in ASM that could handle swapping like putting one element in front of another? It's hard to think about that when those should be functions with their own local variables on the stack (C style) and when it's easy to mix it up in ASM due to the lack of CPU registers.
I have the pointer to the first entity to render and a pointer to first entity to execute. Each entity has its pointer to the next entity to render and a pointer to the next entity to execute. The last entity in a list has the pointer to next equal to zero.
Code:
g_instFirstToExecute = $0012 ; Index in heap of the entity
g_instFirstToRender_Low = $0013

I've decided to not use typedef struct style in this like in the first post (because I learned that from NES's poor indirect indexing explained by ROM City Rampage maker), but arrays of entity members where the entity index in the heap is addressed with X like this
Code:
f_inst_ZeroPosition:
; Set position of an entity to zero
; Assumed that CPU register X had been loaded with the entity index in the heap
lda #$00
sta (inst_X_Subp, X) ; Fraction of X coordinate in subpixels
sta (inst_X_Pixl, X) ; Integer part of X coordinate in pixels
sta (inst_X_Page, X) ; High byte of the integer part of X coordinate
sta (inst_Y_Subp, X)
sta (inst_Y_Pixl, X)
sta (inst_Y_Page, X)
rts

But back to the subject. A function for placing one entity in front of or behind another should look like this in C:
Code:
void inst_PutInFrontOf(uint8_t a, uint8_t b);
void inst_PutBehindOf(uint8_t a, uint8_t b);

For a thing like this, I think I should need a high level assembler like NESHLA, but there's no documentation on how to program with that. Can you find me some? Or should I use C instead? This ASM work will be too tough. If only there was an AVR microprocessor with Von Neumann architecture with its 32 GP CPU registers 3 of them pairs for indirect addressing plus the opcodes for indirect load and store with displacement.

P.S. Is it even normal to think about quitting such a project because it's too retro technically-wise?


Top
 Profile  
 
PostPosted: Wed Jul 29, 2015 5:59 pm 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1251
8bitMicroGuy wrote:
I was thinking that in my Mario clone game all objects should interact with each other like Goombas colliding with each other or accidentally stomping each other or have one turtle shell roll and touch a moving platform which bounces it off or lifts it up.

That's all possible. The worst that could happen is your game slows down some with like 8 objects on screen. You could do a lot of things to mitigate slowdown, like not running the non important collision for every object every frame. I believe Super Bat Puncher does this for enemy-enemy collisions, and you could do it for something like Goombas bouncing off each other. It's not all that important if they're inside each other for a few frames for that.

Even checking every collision every frame is not necessarily the worst. Just remember that with three objects...

Object 1 checks against object 2.
Object 1 checks against object 3.

Object 2 checks against object 3.

Object 3 checks against nothing. (The other two objects already checked for collision with it)
Quote:
It's hard to think about that when those should be functions with their own local variables on the stack (C style) and when it's easy to mix it up in ASM due to the lack of CPU registers.

You can use variables on the stack pretty much like C. I'd have to write some tests before I wrote how, though. I can think of a few ways, but I can't type them blind and be sure they'd work.

How I typically manage is two sets of temp variables. Let's call them temp and ltemp. temps are super volatile. They're free to be changed in every subroutine without regards to others (well almost). For subroutines that call other subroutines, and need things preserved across calls, I use the ltemps. At the point, you do have to check if the function you're calling changes the same ltemp and then use a different one. But it doesn't happen too often. I do it that way mainly for performance, though.

You could also just use pla/pha for variables across subroutines that call other subroutines. (Which is slightly different than the C style thing I'm talking about above.)

Quote:
I've decided to not use typedef struct style in this like in the first post (because I learned that from NES's poor indirect indexing explained by ROM City Rampage maker), but arrays of entity members where the entity index in the heap is addressed with X like this

Why not just use something like this?
Code:
;Defines
objectnum = 20;Max number of objects
spriteramstart = $0300

inst_X_Subp = spriteramstart+objectnum*0
inst_X_Pixl = spriteramstart+objectnum*1

inst_X_Page = spriteramstart+objectnum*2
inst_Y_Subp = spriteramstart+objectnum*3
inst_Y_Pixl = spriteramstart+objectnum*4
inst_Y_Page = spriteramstart+objectnum*5


Code:
f_inst_ZeroPosition:
; Set position of an entity to zero
; Assumed that CPU register X had been loaded with the entity index in the heap
lda #$00
sta inst_X_Subp, X ; Fraction of X coordinate in subpixels
sta inst_X_Pixl, X ; Integer part of X coordinate in pixels
sta inst_X_Page, X ; High byte of the integer part of X coordinate
sta inst_Y_Subp, X
sta inst_Y_Pixl, X
sta inst_Y_Page, X
rts


X = 0 is the first object
X = 1 is the second object
etc.
X = 19 is the last object.

No indexed. This way also allows you to access object info with Y.

For instance,
Code:
ldx #0
ldy #1

lda OBJwidth,x
cmp OBJwidth,y
bcc object0issmaller


So if you needed to put one object in front of another, you could jsr to inst_PutInFrontOf with the object you want to place in front in X, and the object you want to be behind in Y.

And how you'd write inst_PutInFrontOf depends on what behavior you decide as far as rendering.

If I were you, I'd just make a game and worry about this stuff when it comes up. Trying to plan an engine that covers everything will make you spend time planning for things you may never actually need.

And if a poof and a door are active at the same time, well them's the breaks. There are ways around that, and you'll think of them practically once you've written some code.

My game's entity management is basically what I've described above. I have X object slots, and RAM is reserved for them at compile time. I run through the object list three times (Tokumaru would yell at me :wink: ). My player characters are always the first objects processed, and knowing this allows special exceptions. However, the reason I go through the object's list more than once is to cover some special cases (like moving platforms).

In my game objects can be grabbed and carried. If Player 1 (processed first) ran and rendered himself in one step, he'd be in the wrong position because player 2 who grabbed him (processed second) would move the same frame. So... it would become player 2's responsibility to be rerender player 1.

My main philosophy as far as my object management system is that an object almost NEVER affects another object.

So I go through once to resolve all object's positions. I go through again to resolve grabbing. (All object's positions were final, So a grabbed object (or a grabbed grabbed object [or a grabbed grabbed grabbed object]) could have their positions set. I go through a third time to actually render the objects.

In this way, it doesn't matter which object is processed first as far as grabbing. There's never any of that frame behind stuff you often see on moving platforms. It's possible to do what I do in two or one loop through the objects, but I don't for (long winded reason here). One less long winded reasons is that it allows for some hacky stuff. (Some objects do things that aren't related to rendering in their render step. For instance... an object can detect it has been grabbed in its render step, and move itself back to its position before the grab before rendering. This allows the player to trigger a switch with the grab button with no new written behavior.)

Each object in my game has a set of things that it reports to other objects in a single bit.

Stuff like: Object can be bounced off, object can be stomped, object is horizontally collision enabled, object can be grabbed.

And each object has a "mailbox" that allows an object to tell it things. So my player can stomp another object. It will either bounce or not depending on can be bounced off bit. My player will then tell the object it was stomped by setting a bit, rather than say... setting that object's y velocity to 64.

It is the responsibility of an object to update only itself. (There are hacky exceptions, of course!)

So... I have a crate that bursts open when stomped. It checks its "was stomped" bit and destroys itself if set.

I have a ball that's just sent downward without being destroyed. It checks the bit and does that if set.

I have an object planned that will just be toggle ON/OFF if stomped.

The player object has NO idea what of those things the other object is doing, or even if ANYTHING is happening.

As well, enemies can stomp and grab each other. They don't know WHAT stomped them because it pretty much never matters. An object DOES know what's holding it, so that it can set its position properly.

The fact that objects aren't all that aware of each other, means that most of the RAM I've reserved doesn't need to be generic between them. The type bits, position, X/Y velocities and grab bytes are the main ones. Each object also has like... ten bytes it can use for whatever it wants, because the other objects will never touch it. (Even things like animation frame or metasprite don't need to be generic. I can have objects that aren't animated or whatever.) Technically the grab bytes are free to be used for whatever too, as long as the object NEVER reports itself as grabbable. (I uh... use those bytes for another purpose at least once for an object's death animation. But that's hacky.)

The reason my players are always created/processed first (game supports co-op) is so that object order BARELY affects players. For instance... if an enemy was processed in between two players, it might treat those players different. Player 2 (processed second) stomps and kills and enemy in the same frame player 1 (processed first) walks into it. In that case, player 1 gets hurt. Swap the processing order? Player 1 is safe. I do many things to avoid unfairness between players. Even same frame grabs are handled.

Object processing order CAN make a difference with how the enemies act to each other, but the result of the interaction affects each PLAYER the same.

So there are some notes on my Game Entity Management.

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Thu Jul 30, 2015 6:09 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 20783
Location: NE Indiana, USA (NTSC)
8bitMicroGuy wrote:
I keep forgetting that NES is very weak so I don't know will it even be able to handle that stuff. I estimate to have the maximum of 16 active entities on screen at the same time. What do you think? What does SMB3 have for example?

The middle of World 3-7 (see list of maps) is a slowdown-fest. That's what SMB3 has.

Quote:
P.S. Is it even normal to think about quitting such a project because it's too retro technically-wise?

A simpler project might help you build confidence in your skills.


Top
 Profile  
 
PostPosted: Thu Jul 30, 2015 11:46 am 
Offline

Joined: Sun Mar 08, 2015 12:23 pm
Posts: 287
Location: Croatia
What if there's something firing shells or blue blocks? What if it's that Buster Beetle and not Mario? How is the game gonna know to who to give points gained from the shell kicking the enemies? If the game is 2 player maybe, the game needs to know who to give the points to.


Top
 Profile  
 
PostPosted: Thu Jul 30, 2015 1:07 pm 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1251
I actually wrote a thing, but before I post it I have an honest question: Have you made a practical game before? (I don't mean an NES game. It could be any kind of game that's really, actually playable.)

I don't ask this to be mean, but I don't see your questions as unique to nesdev. So I ask because I want to know if you're asking because you specifically want to know how I'd do these things in my engine, or if you're inexperienced in gamedev. Maybe another reason? Whatever is fine, but the answer I was going to give may not have been helpful for why you're asking.

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Fri Jul 31, 2015 6:15 am 
Offline

Joined: Sun Mar 08, 2015 12:23 pm
Posts: 287
Location: Croatia
I made a game in Game Maker Studio that used my own physics and animation. It didn't have any levels or programmed enemies, but it was just a plain engine.
However, managing objects and resource I/O and that stuff which would be in Game Maker Studio's engine is not something I've ever done. Only been imagining it how to make my own free version of it free from any EULAs. But I never succeeded.

Also, I've had finals and the high school was over and it all made me tired and for some reason, I've lost inspiration. I thought that maybe some ideas that you might give me might get me up to think more. What I'd really want is to talk to someone over Skype while sharing my screen as I code while you help me by telling me what could be better or to put this or that. I've always wanted to work with someone and that's when I've been most productive. However, it was mostly their projects and not mine because they never wanted to work on more projects at the same time to avoid losing inspiration and when I ran into serious bugs in my program, I ragequitted. It would have taken me 3 times longer to fix the bug than how much it had taken to write the program in the first place.

So would someone please help me? I can PM my Skype name to you. Just note that I'm in Europe so it will be hard for all Americans to get me at the right time due to timezones.


Top
 Profile  
 
PostPosted: Sun Aug 02, 2015 11:45 am 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1251
I know that feeling. Way, way, way back when I started I used a program called The Games Factory that did many things for me. It was hard for me to understand how to implement those things when I started programming without it. I'm not personally available for live help, though.

To break down object entity management, I start with a linked list described by Tokumaru in a couple of posts:

Here you can read about how you'd go about getting an unused index for a new object, and freeing an index on object destruction.

And here you can read about how you'd initialize the list.

The topic those linked posts are in may be of interest to you in general.

I described in this post how you would add and access RAM for each object. (Note that by changing objectnum to another number, it will automatically make everything use more or less RAM at assemble time.) That post also shows how to write to RAM of a specific object. (X or Y is the object index you're editing, sta ,x or sta,y write to that index.) You could write a collision routine where X is one object's index, Y is another. So two objects can interact with each other quite easily without register switching.

To run through the object list, you do something like this:

Code:
   ldx #objectnum-1
spritestart:   
   lda OBJobjectexists,x
   beq spriteend
   
;Some way to run the specific object's code

spriteend:
   dex
   bpl spritestart


If you're familiar with languages like C, this is a bit like a for loop.
Code:
for(int x = objectnum-1; x >= 0; x--){

}

The reason it counts down instead of up is to avoid an extra compare.

The some way to run the specific object's code is a bit more tricky... Upon object creation, I actually just store the address of the object's code in it's RAM and jump indirectly there, more or less.

Something like this for creation:
Code:
   lda #low( PlayerCharacterInitialize );Loading the low byte of the player character's initialize routine
   sta tempLo;Note the syntax of how to get the low byte of an address can change assembler to assembler
   
   lda #high( PlayerCharacterInitialize  );Loading the high byte of the player character's initialize routine
   sta tempHi;Note the syntax of how to get the high byte of an address can change assembler to assembler
   
   jsr createobject
   
   jmp somewhere
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

createobject:
   ;JSR here with the initialize address of the object you want to create in tempLo and tempHi
   
   ldx OBJfreeslot;If there was no slot
   bmi co.fail;we're not allowed to create another object
   
   ;Next free slot setup
   lda OBJaddrlow,xDead objects don't really need their RAM, so I use
   sta OBJfreeslot;their low byte to store the next slot
   
   
   lda tempHi
   sta OBJaddrhigh,x
   
   lda tempLo
   sta OBJaddrlow,x   
   
   lda #$FF;non zero
   sta OBJobjectexists,x
co.fail:
   rts;


If you go with this method of creating objects, ";Some way to run the specific object's code" becomes this:

Code:
   lda OBJaddrhigh,x
   sta tempHi
   
   lda OBJaddrlow,x
   sta tempLo
   
   jmp (tempLo);Note, for this to work, tempHi needs to be immediately after tempLo,
   ;and both need to be on the zero page
   ;
   ;For instance, tempLo = $00, and tempHi = $01


To destroy an object in its most basic form:

Code:
destroyobject:
;Destroy the object in X
   lda OBJfreeslot
   sta OBJaddrlow,x
   stx OBJfreeslot
   
   lda #$00;Handled below
   sta OBJobjectexists,x
   
   rts


Then, each initialize routine must write the address of the main routine for the object and end in jmp spriteend (that is where the loop is). Also, if it changes X, it must change it back to the object's index BEFORE it jmps back or very bad things will happen.

Code:
PlayerCharacterInitialize:
   lda #low( PlayerCharacterMain );Loading the low byte of the player character's main routine
   sta OBJaddrlow,x
   
   lda #high( PlayerCharacterMain  );Loading the high byte of the player character's main routine
   sta OBJaddrhigh,x

   ;Other initialize junk here

   jmp spriteend


Each main routine needs to end in spriteend as well, and also must make sure X is the object's index before jmp spriteend
Code:
PlayerCharacterMain:
;Code to run the object here.

   jmp spriteend


If you want an object to check collisions with other objects:

Code:
   txa;Transfer our own index into Y
   tay;And go to the next index, because we do not need to collision check
   dey;Objects that have already been processed
   bmi objectcollision.done;If we get a negative number, there are no more object
collision.start:
   lda OBJobjectexists,y
   beq nextcollision;If the object doesn't exists, we don't want to collide with it

   jsr generalspritecollision;This collision checks the object in Y with the object in X

nextcollision:
   dey
   bpl crate.collision.start
   
objectcollision.done:


It's pretty much like the for loop above, except it only works on indexes lower than the current object, rather than all objects.

Those are all the main building blocks of how I handle objects. (I do things slightly differently for speed and such, hopefully I didn't make any mistakes that won't work editing those things for this post... :? ) Things like making enemies be created when you scroll them onto the screen is a slightly more complicated thing to understand. But if you begin to write scrolling, you'll likely understand how it might be done. For games without scrolling, when the level is loaded, you read your data to find out what enemies to create and where. Then you create them there.

In response to your questions about how to handle things with shell kicking and such, I could make another very long post.

But the really simple thing to tell you are that if you can't get information you need from what you have already in the program, you add a way to get that information. If the enemy needs to know what last kicked it, that object needs some space in its RAM reserved for that, plain and simple. Then the players could set those bits on another object when they collide with it. If an enemy needs to know whether or not an object is the player character, you add a bit to differentiate the two. The games doesn't know how to do anything until you tell it how. But in a more positive statement, the only thing you need to do to make the game do something is teach it how to do it.

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Tue Aug 04, 2015 9:54 am 
Offline

Joined: Sun Mar 08, 2015 12:23 pm
Posts: 287
Location: Croatia
Every function that I would make would actually need to use stack for parameters and automatic variables, but getting to them would be a pain because of losing the X register. Saving it into a global variable is bad because other interrupts might clobber it, plus NMI will always bypass the global interrupt disable flag which leaves you in no safe position to do atomic operations. This is very complicated and hard. As I went to see how Sega Master System specs, it has 4bpp tiles! That will give me a lot of colors. Plus, it has a processor like Z80 which has more CPU registers. Maybe I should move over there. Too bad there's no duty cycle change in the sound, but oh well, there's no such a thing as a perfect 8-bit console. Or is there? I was expecting that an 8-bit console would be somewhat powerful like an AVR, but AVR sucks because of not having an enough fast and functional and correctly documented External Bus Interface and being Harvard which causes C functions to need to copy all constants into RAM and stuff... Man oh man... I've wasted my whole one year on working on the final project for school with an AVR microcontroller and I actually pushed it to the max and got 2 A-s. Because of that, I've forgot how to program anything else. Maybe making a game console with a CPU designed by me on FPGA would be perfect. It would have ASM in my own style and all attributes that I like. After all, I studied in school about PC components made from TTL circuits. I might do it.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 36 posts ]  Go to page 1, 2, 3  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 5 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group