I'm not sure what you're referring to by portal mechanics, sorry. In my mind a portal is a "hole" in geometry that links through to another piece of geometry. That geometry may be a continuation of that part of the level, but various special effects can be achieved if the other side of the portal to another part of the level. I'm not sure how I could implement this using this engine. The BSP tree sorts geometry from front-to-back when you place the camera in a fixed position, but stepping through a portal would move the effective camera position and "break" this ordering.

On a desktop PC you'd have the benefit of a stencil buffer for clipping around the portal and a z-buffer for occlusion so you wouldn't have these difficulties.

If I've misinterpreted what you were saying, sorry. Sad
I was thinking a portal mechanic like the one in Portal, where the player can shoot linked apertures into the walls, and entering one returns the player to a different place in the map. I can certainly understand that it would be hard or impossible to show the linked portion of the level via the portal, but I'm sadly not getting why it would be difficult to move the player arbitrarily. Smile
Ah, so more like the portal gun from the Killer Quake mod? (In appearance, at any rate; Killer Quake's portal gun only lets you jump through thin walls). Some sort of wall-aligned sprite support would help here, but I'm not sure how I'd handle the perspective-correct mapping...

A different game mechanic which would be somewhat easier to render would be a personal teleporter. The one in Quake 3 Arena appears as a pickup and, when used, sends you to a random location on the map. It's a single-use item and is a handy "panic" button. This could be logically extended to allow you to "drop" the teleporter (which would appear as a sprite sitting on the floor) and hitting the teleport button would alternate you between your current position and the dropped position.
benryves wrote:
Ah, so more like the portal gun from the Killer Quake mod? (In appearance, at any rate; Killer Quake's portal gun only lets you jump through thin walls). Some sort of wall-aligned sprite support would help here, but I'm not sure how I'd handle the perspective-correct mapping...

A different game mechanic which would be somewhat easier to render would be a personal teleporter. The one in Quake 3 Arena appears as a pickup and, when used, sends you to a random location on the map. It's a single-use item and is a handy "panic" button. This could be logically extended to allow you to "drop" the teleporter (which would appear as a sprite sitting on the floor) and hitting the teleport button would alternate you between your current position and the dropped position.
Sure, that could be a lot of fun too. What you're describing is almost identical to the Unreal Tournament "translocator" device, as a matter of fact.
Ah, it is similar in a way. Smile

I'm not sure whether I'll be able to build a game on top of the engine (that may be a job for someone else - I'm not so hot on the creative side of things, in terms of level/graphic design or AI) but I'll try to get the rendering engine as good as I can make it and add some basic physics (collision detection, walking up and down steps, doors/lifts etc) that could be used as a base.

I also need to find a better way of building levels. The level in the demo was sketched on paper and then converted into C# by hand. The final results are a little terser but it's a right pain to have to edit levels by hand. Any tips on how to compile a BSP tree from some input geometry would be gladly appreciated. Wink

I have been considering writing a tool to convert DOOM WADs to the required level format. I could then use a DOOM node builder to compile the BSP tree. I'm not sure how well this would work in practice, though, as I use 8.8 precision throughout (DOOM uses 16.16) and I have a limit of 256 vertices per level.
 


Work has left me with little time to work on my own projects of late. I have, however, added simple collision detection to Nostromo, and you can read more about this and download a demo from my site. Failing that, click on the above screenshot to view an animated GIF.
benryves wrote:
 


Work has left me with little time to work on my own projects of late. I have, however, added simple collision detection to Nostromo, and you can read more about this and download a demo from my site. Failing that, click on the above screenshot to view an animated GIF.
Yup, saw that on your Facebook and blog; it looks great! It occurred to me finally to be curious what you use to estimate the FPS; might I be so bold as to inquire?
Thank you. Smile I use a timer interrupt service routine that increments a counter every time it is fired. This primarily allows me to scale movement by time to keep it roughly consistent regardless of the current frame rate. As a side-effect, dividing 335 by the number of ticks spent rendering each frame provides a rough estimate of the current frame rate. I reached that value by trial and error (comparing it to the value reported by Wabbitemu). It's somewhere between the timer speeds on the TI-83+ and TI-83+ SE, so is reasonably accurate.
benryves wrote:
Thank you. Smile I use a timer interrupt service routine that increments a counter every time it is fired. This primarily allows me to scale movement by time to keep it roughly consistent regardless of the current frame rate. As a side-effect, dividing 335 by the number of ticks spent rendering each frame provides a rough estimate of the current frame rate. I reached that value by trial and error (comparing it to the value reported by Wabbitemu). It's somewhere between the timer speeds on the TI-83+ and TI-83+ SE, so is reasonably accurate.
I figured it was probably something like that, very nice! And having the movement distance be inversely proportional to the framerate by having it governed by the interrupt is a superb idea; I don't know if I would have thought of that. I can't wait to try what is probably a fruitless endeavor and make Nostromo a multiplayer CALCnet2.2 FPS. Wink
KermMartian wrote:
And having the movement distance be inversely proportional to the framerate by having it governed by the interrupt is a superb idea; I don't know if I would have thought of that.
It's directly proportional to the amount of time that has elapsed since the game's logic code was run. It's one of the more common ways to handle game logic; the other is fixed time-step. One problem with scaling movement by time is that if there's a long time between updates the player may end up moving a very great distance and walk through thin walls; if the time elapsed is 30 ticks, rather than move the player thirty units once it may be more practical to move the player one unit thirty times.

Edit: I've found that this issue can be triggered in the Nostromo demo. Set the calculator to 6MHz mode, then move near a wall and look into a room that drops the performance to a few FPS. Now hold the back key and either of the strafe keys (this gives you the largest movement vector) and rotate the view - if you've got it right you should be able to pop through the wall.

Quote:
I can't wait to try what is probably a fruitless endeavor and make Nostromo a multiplayer CALCnet2.2 FPS. Wink
That's provided I can get the rendering and basic game engine in a decent state. There's a long way to go yet... Smile
benryves wrote:
KermMartian wrote:
And having the movement distance be inversely proportional to the framerate by having it governed by the interrupt is a superb idea; I don't know if I would have thought of that.
It's directly proportional to the amount of time that has elapsed since the game's logic code was run. It's one of the more common ways to handle game logic; the other is fixed time-step. One problem with scaling movement by time is that if there's a long time between updates the player may end up moving a very great distance and walk through thin walls; if the time elapsed is 30 ticks, rather than move the player thirty units once it may be more practical to move the player one unit thirty times.
Mmm indeed, that makes sense. I meant that the lower the framerate, the further the player is moved per frame, but as you say, it makes more sense to do the former than the latter.

Quote:
Edit: I've found that this issue can be triggered in the Nostromo demo. Set the calculator to 6MHz mode, then move near a wall and look into a room that drops the performance to a few FPS. Now hold the back key and either of the strafe keys (this gives you the largest movement vector) and rotate the view - if you've got it right you should be able to pop through the wall.
Oh dear; what is your proposed fix?

Quote:
Quote:
I can't wait to try what is probably a fruitless endeavor and make Nostromo a multiplayer CALCnet2.2 FPS. Wink
That's provided I can get the rendering and basic game engine in a decent state. There's a long way to go yet... Smile
Hey, looks pretty impressive to me. Smile Keep up the good work.
Quote:
Quote:
Edit: I've found that this issue can be triggered in the Nostromo demo. Set the calculator to 6MHz mode, then move near a wall and look into a room that drops the performance to a few FPS. Now hold the back key and either of the strafe keys (this gives you the largest movement vector) and rotate the view - if you've got it right you should be able to pop through the wall.
Oh dear; what is your proposed fix?

The easiest fix would be to break the player's movement into smaller steps. A more geometrically correct one would be rather complex (and slow).

Quote:
Quote:
Quote:
I can't wait to try what is probably a fruitless endeavor and make Nostromo a multiplayer CALCnet2.2 FPS. Wink
That's provided I can get the rendering and basic game engine in a decent state. There's a long way to go yet... Smile
Hey, looks pretty impressive to me. Smile Keep up the good work.
Cheers. I will as and when I have the time!
I've done a little work on the project and added another room:

     

     

     

     

The map is now over 5,000 bytes and the rendering engine is approaching 7,000 bytes. Unfortunately, I think I've nearly run out of available RAM; if I enlarge the buffers slightly I start getting strange rendering glitches. This could be caused by the stack overwriting some of the dynamically-allocated tables (or it could be another issue, I'm not sure) - until I've investigated this bug further I'm afraid I'm not going to release a demo. I could turn this into an application, of course, but quite a lot of the code uses self-modifying tricks to boost performance.

Performance is also extremely poor in this build. I suspect that it's due to the BSP tree being poorly balanced; each room is fairly well partitioned internally, but with each room comes a more lopsided tree. The result is that when you're inside a room and can't see any other room performance is relatively good, but if you can see more than one room at a time performance can slow to a crawl (2FPS in places).

I hope to add debugging features which will allow me to check where the bottlenecks are. In their simplest form these could simply add up the total number of BSP tree nodes, subsectors and walls that are handled each frame.

One feature I have added is better handling of moving sectors. This currently maintains a list of sectors which are currently moving, with a pointer to the variable to update, the velocity of the movement, the minimum value and the maximum value. When the camera moves from one sector to another an event is fired specifying which sector the user has moved from and which they have moved from. The game logic then uses this information to tell doors to open or shut and a platform to raise or lower. This feature is demonstrated in this new animated screenshot. (Watching the screenshot makes me deeply jealous of TI-83+ Silver Edition owners). Sad

One other feature which is not demonstrated (yet was quite a lot of work) is the ability to render more than one sprite in each convex sub-sector correctly. The convex sub-sectors are already sorted front-to-back by the BSP tree, so when rendering sprites these are drawn in the reverse order so sprites in the foreground occlude sprites in the background. To draw multiple sprites within each sub-sector correctly they need to be sorted from back-to-front, which I've done using a simple insertion sort. In addition, sprites are now assigned to sub-sectors using a linked list, which means that it will be possible to remove a sprite from one sub-sector and move it into another. This means that sprite objects will be able to be moved around the level, whereas they are currently static.
Awesome work as always, Ben. The performance does look like a little degraded, but still seems quite useable (of course, I have a silver edition or two, so that helps). I played the last demo that you released, and I was pretty happy with it. It obviously won't be as fast paced as other games with similar styles, but I still think it played plenty fast enough. Though I'm lost a bit in the technical details, it seems that an app may be the way to go; can you not do self-modifying code in an app?
merthsoft wrote:
Awesome work as always, Ben. The performance does look like a little degraded, but still seems quite useable (of course, I have a silver edition or two, so that helps).

Thank you! I'm sure if a competent Z80 programmer had a go at this they'd be able to get much better performance out of this. If anything this can serve as a rough indication that BSP tree-based worlds work pretty well on the calculator!
Quote:
I played the last demo that you released, and I was pretty happy with it. It obviously won't be as fast paced as other games with similar styles, but I still think it played plenty fast enough.

When playing it on hardware you get the advantage of motion blur, too. The cramped control scheme of the calculator would probably not lend itself very well to a fast-paced game, in any case, so there's that as an advantage.

Quote:
Though I'm lost a bit in the technical details, it seems that an app may be the way to go; can you not do self-modifying code in an app?

Self-modifying code is code that overwrites itself at runtime. This requires, however, that the program is running from RAM; you can't overwrite ROM (at least not easily or quickly). One workaround I have used in the past is to split the code into bits that run in RAM and bits which run in ROM and copy the code to RAM as required but this is a bit clunky.

An example of code that is self-modifying is the code to calculate the Y intercept of a line segment (returning the result in HL). This is an expensive operation and can be called multiple times per line segment so I use a memoization trick:


Code:
GetYIntercept:
   jr $+1
   nop
   
   ; Result = Start.X - (Delta.X * Start.Y) / Delta.Y
   
   ld de,(Delta.X)
   ld bc,(Start.Y)
   call Maths.Mul.S16S16
   
   ld a,e
   ld b,h
   ld c,l
   
   push de
   
   ld de,(Delta.Y)
   call Maths.Div.S24S16

   ld hl,(Start.X)
   
   pop de
   ld a,d
   xor e
   jp m,+
   neg_bc()
+:   add hl,bc

   ld a,$21 ; LC HL,nn
   ld (GetYIntercept+0),a
   ld (GetYIntercept+1),hl
   ret


Before each wall is handled the cached value is cleared with the following:

Code:
   ld hl,$0118 ; JR $+1
   ld (GetYIntercept+0),hl
Ah, ok, I understand. I wasn't sure the technical details behind apps, so I didn't realize it was running in ROM. I assumed it copied stuff into RAM to run it (just shoved it into whatever address is used to run code anyway). That's a nifty little routine. By the way you say it, you can continue to do this by copying into RAM as you see fit, though, right? I assume that'll have a small performance hit, but copying memory, I imagine, is relatively speedy (at least O(n)).
merthsoft wrote:
Ah, ok, I understand. I wasn't sure the technical details behind apps, so I didn't realize it was running in ROM. I assumed it copied stuff into RAM to run it (just shoved it into whatever address is used to run code anyway).

Ah, no. Programs run from RAM starting at $9D95 up to $BFFF (data can appear at $C000 or above but any attempts to execute code in the $C000..$FFFF region causes the calculator to reset - the "8K" limit). Applications run directly from ROM in the $4000..$7FFF region.

Quote:
That's a nifty little routine.

Heh, thanks, and LC HL,nn is meant to be LD HL,nn of course. Razz

Quote:
By the way you say it, you can continue to do this by copying into RAM as you see fit, though, right? I assume that'll have a small performance hit, but copying memory, I imagine, is relatively speedy (at least O(n)).

I tend to copy all routines that may need to run from RAM into RAM at program start. The issue then is that I'm still taking up RAM, when the aim was to move as much of the program into ROM as possible. Unfortunately, the routines that use SMC tricks tend to be the largest and most unwieldy in the first place.

I'll need to experiment.
You should encourage calc84maniac to peruse some of your routines for optimizations. He's sneaky like that.
For what it's worth, a lot of the Celtic III and xLIB compatibility routines on the third page of Doors CS use self-modifying code by copying the relevant routines to the chunk of RAM at $8000, which you can use as long as you rebuild the app page table afterwards.
elfprince13 wrote:
You should encourage calc84maniac to peruse some of your routines for optimizations. He's sneaky like that.

I'd be happy if he could do that, but the code needs some serious tidying up first. I've started and shaved a few hundred bytes off already, but there's still much to do. Smile

KermMartian wrote:
For what it's worth, a lot of the Celtic III and xLIB compatibility routines on the third page of Doors CS use self-modifying code by copying the relevant routines to the chunk of RAM at $8000, which you can use as long as you rebuild the app page table afterwards.

Ah, that sounds good. I'm hesitant to go down the application route unless absolutely necessary as I don't have any way to install applications on my calculator at the moment!

The BSP tree sorts geometry from front to back and is walked along until either every column on the display has something drawn on it or you run out of tree nodes. I suspect that if you have a large room in front of you and a small, complex room behind you performance drops as the renderer is having to deal with lots of subsectors outside the viewing area before getting to the far-off walls in the room you're looking at. What would be good is if I could find a quick way to reject BSP nodes that are known to be out of sight. PVS may work (indicating BSP nodes which are invisible from a particular subsector) though wouldn't take camera rotation into consideration. Bounding rectangles didn't work at all well (the additional code to transform and check the rectangle for each noed slowed the engine to a crawl). Bounding circles may be faster, as that's only a single point to transform and checking is a bit easier. Hrm. I'd be grateful to hear any suggestions...
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 3 of 5
» All times are UTC - 5 Hours
 
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 vote in polls in this forum

 

Advertisement