Page 2 of 3

Re: camera movement (and object spawning)

Posted: Tue Aug 11, 2015 3:47 pm
by tepples
Khaz wrote:I wonder... Has anybody ever programmed a game that deliberately detects and avoids slowdown? Like put a scanline interrupt right near the bottom of the picture where, if processing isn't complete by then, it cuts short everything remaining somehow to keep a constant framerate, at the expense of some objects not getting updated regularly?
Gradius for NES does the opposite: estimate time to ensure stability even if it means slowdown. Because it displays a bottom status bar on a mapper without a scanline interrupt, it reportedly adds the time needed to handle each object, and if that would put it past where the status bar goes, it delays the rest of the objects to the next frame.
Khaz wrote:I mean I can see engineering it to never go over the limit by design, or to alternate processing between frames
Thwaite processes collisions with explosions every other frame, and it processes missile spawning and villager movement once every tenth of a second (six frames on NTSC, five on PAL), with different things to do in frames 0-4 of each tenth. RHDE also runs a lot of stuff once every tenth of a second. Super Mario Bros. has a task scheduler with a 21-frame sequence (18 on PAL); because a level can end only on a 21-frame boundary, a new TAS has to improve the time for each of the levels by that much.

Re: camera movement (and object spawning)

Posted: Tue Aug 11, 2015 3:57 pm
by UnDisbeliever
Khaz wrote:I'm having trouble deciding how to spawn enemies in my own game. I'm still working with the 64x64 rooms (with size-16 tiles) plan which I kinda like as a model for pretty much the whole game. I'd thought I'd be able to get away with just spawning everything on entry, but it really only takes a handful of objects in existence before things start to lag.

One thought I've had is to spawn everything on entry, but keep things deactivated until you get close enough. My system can handle 64 objects, which shouuuuld be enough to cover a whole room? Though that includes projectiles and platforms and pickups and everything, so then again maybe not. (P.S. I hate sprite platforms so much.)
Castle Platformer loaded all enemies (maximum of 64) into RAM on level start.
There was a active scrolling window twice the size as the screen in which entities will be processed.

If an enemy moves outside it it is moved into the deactivated list. If a projectile moves outside it then it is removed from play.

When the active window scrolls onto an deactivated entity, it is activated. The entity's graphics are loaded into VRAM and CGRAM.


This caused an issue when both the enemy is moving left and the player is also moving left (but slower than the enemy). The enemy would move into the deactivated list, pause a few frames, then become activate again. This shifted it's position and if there were multiple enemies in a row they would begin bunch up together.

I initially tried to fix it by doing what Mario Bros 3 does and have the enemy face the player upon activation, but it also broke enemies rows.


I'm going to experiment a bit with the next engine. I think having three scrolling windows; one to deactivate the entities (the largest, 2x screen), one to activate the enemies (1.5x screen), one to load graphics (the smallest, 32px larger then screen) will minimise or outright eliminate this bug. Even if it doesn't it should matter as much in a Metroidvania or Megaman style game.

I am also going to experiment with grouping. Each enemy could exist as part of a group. If one enemy in the group is activate they all are. Only if the entire group if outside the actiave window will all enemies in the group be deactivated.

Re: camera movement (and object spawning)

Posted: Tue Aug 11, 2015 4:02 pm
by Kasumi
Khaz wrote:I wonder... Has anybody ever programmed a game that deliberately detects and avoids slowdown?
I believe Sonic the Hedgehog attempts to avoid rendering the screen to "catch up" during lag frames. (It does some other things too. See this page under lag.) I'm sure there are many others, but that's the one I know offhand. My game on NES detects lag frames and skips the vblank wait for the next frame if a lag frame is detected to avoid running at 30 FPS in those cases. (There are certain tasks you can get done before you have to wait.) However, I'm not sure about that behavior. Initially, it just always skipped the wait for vblank on lag frames, but then the game basically just got stuck on the wait loop before the things you have to do rather than what was supposed to be the main loop. It's a weird thing in general. If I just rearrange the logic with a single wait before the "need" things, I'd have exactly the same 30 FPS slowdown I'm trying to avoid with a slightly different order of game logic and no lag reduction. *shrug*

As far as enemies and the camera, if your game doesn't require require enemies to be respawn, just don't respawn them. For a Gunstar Heroes type thing, constantly spawning things is a different case. And in that case, I don't think the camera really affects that type of spawning.
I'm not sure how to handle when things go off screen, whether to disable them or despawn them completely, or just ignore collisions... One way or the other, constantly comparing everything to the screen/player position to see if it should be active or not sounds like a lot of extra processing on its own.
My game doesn't have a lot of wiggle room as far as what collision info is "loaded" offscreen. I currently disable an enemy if it travels "far" offscreen. If it remains offscreen for X frames, it's despawned.

The problem with disabling completely if you can do things like fire a lot of shots offscreen. They'll get frozen. Then you walk a bit closer, and some poor enemy that was also frozen out there will take like 12 bullets at once. (This is a common exploit in the original version of Spelunky.) Or you throw a ball and it travels offscreen while airborne. The player waits, walks after it to find it hasn't landed during the wait time when it should have.

And comparing things to the player or camera position isn't a lot of processing at all. It's maybe two subtractions per frame per object. (If absolutevalue(enemypos-cameramiddle) > 256, despawn.) You could do it with an AND and a CMP too depending on what distance you choose.

Re: camera movement (and object spawning)

Posted: Tue Aug 11, 2015 4:10 pm
by tepples
UnDisbeliever wrote:Castle Platformer loaded all enemies (maximum of 64) into RAM on level start.
There was a active scrolling window twice the size as the screen in which entities will be processed.

If an enemy moves outside it it is moved into the deactivated list. If a projectile moves outside it then it is removed from play.
Super Bat Puncher for NES appears to have the same behavior.
I'm going to experiment a bit with the next engine. I think having three scrolling windows; one to deactivate the entities (the largest, 2x screen), one to activate the enemies (1.5x screen), one to load graphics (the smallest, 32px larger then screen) will minimise or outright eliminate this bug.
So essentially, you're putting an enemy's distance from the player through a Schmitt trigger. I can see how this sort of hysteresis might help.

Re: camera movement (and object spawning)

Posted: Tue Aug 11, 2015 4:13 pm
by psycopathicteen
Khaz wrote: I'm having trouble deciding how to spawn enemies in my own game. I'm still working with the 64x64 rooms (with size-16 tiles) plan which I kinda like as a model for pretty much the whole game. I'd thought I'd be able to get away with just spawning everything on entry, but it really only takes a handful of objects in existence before things start to lag.
Can we see some examples of code that gets repeated a lot per frame?

Re: camera movement (and object spawning)

Posted: Tue Aug 11, 2015 5:37 pm
by Khaz
psycopathicteen wrote:Can we see some examples of code that gets repeated a lot per frame?
... I think it would be smart of me to implement a CPU usage meter that actually illustrates how much time each object/subroutine is taking up, rather than just total frame time, in order to answer that question better. Generally speaking I'm assuming it's just the sheer volume of object-object collisions I'm testing for that gets out of hand. Especially since I've designed a game where the player can pick their projectiles back up, so you test collisions both with enemies and yourself. And then you have platforms you have to check the same way, and the projectiles and enemies can collide with them too, so if you get a mix of all of those that's quickly becoming tons and tons of comparisons.

I'm sure I can clean things up a lot still, my object collisions are sloppy and basic at the moment. One thing I haven't implemented yet that I will need to is making your projectiles expire, when they're sitting too long and/or when you have too many out (probably just the latter). I was hoping that letting at least eight exist at a time would be tolerable but lately I'm thinking five or six at most.

Thanks to everyone else for the tremendous response though, you've given me a lot of ideas to work with and I feel a bit more confident now. :P

Re: camera movement (and object spawning)

Posted: Tue Aug 11, 2015 6:40 pm
by psycopathicteen
Might as well show it.

Re: camera movement (and object spawning)

Posted: Tue Aug 11, 2015 8:06 pm
by Khaz
psycopathicteen wrote:Might as well show it.
Deciding what "it" is is the hard part of responding to that request. As I've said before I don't want to just post up my entire source code, but picking out one representative part of it is hard.

Here's generally how I check for object collisions. (Note that this chunk assumes the player's hitbox is one tile in all directions from its position.) It seems pretty obvious and I can't really imagine a faster way to do it.

Code: Select all

;============================================================================
; ENEMY PROJECTILE SUBTYPE - 06
;----------------------------------------------------------------------------
	lda.w objSubList6First		;start checking with first object in subtype list
	sta.w registerT

_procNMYProjCollPlyr:
	lda.w registerT
	cmp.w #$FFFF				;If object index is FFFF, we're at the end of the list
	bne _notDoneNMYProjCollPlyr
	jmp _doneNMYProjListCollPlyr

_notDoneNMYProjCollPlyr:
	tax
	
	lda.w $0000+obj0NextTypeObj, x		;fetch next object OF SAME SUBTYPE
	sta.w registerT				;store this in case current object gets terminated during collision

	lda.b obj0XPosn	              ;test position
	sec
	sbc.w $0000+obj0XPosn, x
	cmp #$0010
	bpl _procNMYProjCollPlyr
	cmp #$FFF0
	bmi _procNMYProjCollPlyr

	lda.b obj0YPosn
	sec
	sbc.w $0000+obj0YPosn, x
	cmp #$0010
	bpl _procNMYProjCollPlyr
	cmp #$FFF0
	bmi _procNMYProjCollPlyr

;=========================
	;it's a hit, do stuff

[...]

doneNMYProjListCollPlyr:
Apologies for the misaligned comments. Oh how I wish tabs worked normally.

Re: camera movement (and object spawning)

Posted: Tue Aug 11, 2015 8:06 pm
by tepples
If all projectiles can collide with all actors, then perhaps it might be wise to sort all projectiles by X position so that the collision testing can do a binary search for projectiles near each actor.

Re: camera movement (and object spawning)

Posted: Tue Aug 11, 2015 8:52 pm
by psycopathicteen
You don't need "register T". You could just keep it in X-index. Other than that, your code looks really efficient.

Code: Select all

;============================================================================
; ENEMY PROJECTILE SUBTYPE - 06
;----------------------------------------------------------------------------
   ldx.w objSubList6First      ;start checking with first object in subtype list
   bra _randomlabel

_procNMYProjCollPlyr:

   lda.w $0000+obj0NextTypeObj, x      ;fetch next object OF SAME SUBTYPE
   tax

_randomlabel:
   cpx.w #$FFFF            ;If object index is FFFF, we're at the end of the list
   bne _notDoneNMYProjCollPlyr
   jmp _doneNMYProjListCollPlyr

_notDoneNMYProjCollPlyr:
  
   lda.b obj0XPosn                 ;test position
   sec
   sbc.w $0000+obj0XPosn, x
   cmp #$0010
   bpl _procNMYProjCollPlyr
   cmp #$FFF0
   bmi _procNMYProjCollPlyr

   lda.b obj0YPosn
   sec
   sbc.w $0000+obj0YPosn, x
   cmp #$0010
   bpl _procNMYProjCollPlyr
   cmp #$FFF0
   bmi _procNMYProjCollPlyr

;=========================
   ;it's a hit, do stuff

[...]

doneNMYProjListCollPlyr:
It could be something else like BG-layer collision, or animation-related stuff. Are you still using a DKC-style dynamic animation engine?

Re: camera movement (and object spawning)

Posted: Tue Aug 11, 2015 9:08 pm
by Kasumi
Here's my favorite collision routine for object/object hits: http://atariage.com/forums/topic/71120- ... try1054049

You may have to do some preprocessing for SIZE1+SIZE2-1 if the objects aren't a fixed size, but even with that it's quite fast. Should work in 16bit.

Re: camera movement (and object spawning)

Posted: Tue Aug 11, 2015 10:37 pm
by Khaz
tepples wrote:If all projectiles can collide with all actors, then perhaps it might be wise to sort all projectiles by X position so that the collision testing can do a binary search for projectiles near each actor.
Huh. So you test if it's within the left collision threshold until you find one that is, then test for the right boundary until you go past it, then stop checking? That could indeed save a bit of time. And sorting the list would be as simple as re-linking the objects together... but given projectiles that don't necessarily travel in a straight line, that could be a lot of re-ordering of the list, enough that it might negate the benefits...?
You don't need "register T". You could just keep it in X-index. Other than that, your code looks really efficient.
I think you're right. Can't remember whether it was just that I didn't trust myself not to touch X before the next loop, or if I just didn't realize cpx existed back when I wrote this. This code's from a while back...
It could be something else like BG-layer collision, or animation-related stuff. Are you still using a DKC-style dynamic animation engine?
Yup, still am. And my BG collision may take a fair bit of processing to do, but I've gone over it and over it and I seriously doubt it could be made much faster. That part I'm probably going to have to just work around as-is no matter what. It's not a terrible amount of calculation, but yeah.

Now, my code to collide projectiles with the BG is probably a bit dated and needs serious overhaul by now, but I've been putting that off since it at least works perfectly for now...
Kasumi wrote:Here's my favorite collision routine for object/object hits: http://atariage.com/forums/topic/71120- ... try1054049

You may have to do some preprocessing for SIZE1+SIZE2-1 if the objects aren't a fixed size, but even with that it's quite fast. Should work in 16bit.
Looks pretty good to me! I'll take a closer look tomorrow when I'm not so tired. Thanks for the link!

Re: camera movement (and object spawning)

Posted: Wed Aug 12, 2015 8:43 am
by psycopathicteen
I'm surprised it works without clc or sec.

EDIT:
Your idea of 64x64 tile rooms sounds like a good idea. You can have 6 bits of subpixel accuracy, and 10 bits of pixel coordinates in a single word. If you want to stretch the room to an entire level, you can have the level map scroll itself and still have a big enough spawning window.

Re: camera movement (and object spawning)

Posted: Wed Aug 12, 2015 1:15 pm
by 93143
It looks like the carry flag is guaranteed clear at the end of an iteration unless an overlap is detected, in which case you can clear it in the handler. Seems okay to me...

Re: camera movement (and object spawning)

Posted: Wed Aug 12, 2015 3:11 pm
by Khaz
psycopathicteen wrote:Your idea of 64x64 tile rooms sounds like a good idea. You can have 6 bits of subpixel accuracy, and 10 bits of pixel coordinates in a single word. If you want to stretch the room to an entire level, you can have the level map scroll itself and still have a big enough spawning window.
At the moment I'm just working with 24-bit position values, word for pixels and a byte for sub-pixels. Seemed to keep the math simpler than trying to constantly shift bits over and such, and (so far) I have the room in my object's internal variables. Probably just a tiny gain in processing speed but I'll take anything I can get if I've got the RAM room.