Log In  

Cart #picotroncaster-3 | 2024-04-16 | Embed ▽ | License: CC4-BY-NC-SA
13

Edit: updated to include a few more optimisations

This is a simple raycasting demo for Picotron. I wanted to check out the performance. It now manages 60fps most of the time thanks to optimisation suggestions by @freds72 and @Eiyeron. Rays are scaled to one third of the screen width to achieve this.

It is an implementation of the DDA algorithm, which is more efficient than a basic marching forward algorithm. The basic algorithm is included in the package if anyone is interested.

I used the extended palette to dither/darken the colour based on distance which does help to enhance the visuals.

Controls

Move using the arrow keys.
The 2D overlay map can be toggled on and off with the 'Z' key.
Basic performance stats can be toggled on and off with the 'X' key; A basic copy of Pico-8's Ctrl-P stats.
If you download to Picotron (using 'load #picotroncaster-2') then you can also used WASD to move and <> (,.) will sidestep left and right (these keys don't work in the browser version).

Background

This has been converted from a JavaScript version I built, which itself was a distillation of numerous tutorials on YouTube and other places and in different programming languages. I initially tried to port it to Pico-8 but it was too slow. But it ran buttery smooth in TIC-80 at 60fps, but didn't look as nice as this version due to limited colours.

What I learned playing with Picotron

Although the raycasting itself was relatively slow, rendering the result is very efficient. Picotron could easily render full native resolution (1 ray per horizontal pixel) if it could calculate it fast enough.
There's no built-in Tan function, so had to create it from sin/cos.
The built-in sin and cos functions are faster than the LUA equivalents(math.), but the difference can be mitigated by aliasing the math. function (msin = math.sin etc. Thanks to Eiyeron for that tip)
However, math.sqrt is faster than Picotron's sqrt() function and math.random is also faster than the built-in version (although math.random doesn't work with tables directly).
It seems that key() & keyp() don't work in the browser version, so you need to use btn() & btnp() if you upload a cart to the BBS.

Credits

I took the colour re-mapping code from @luchak's smoke simulator and used it to create the blue gradient for the walls. The original raycasting tutorials were from the following YouTube channels and websites(among others, not all of which are still running): The Coding Train, 3DSage, MaximIvanov, playfulJS.com, lodev.org.

P#146436 2024-04-12 06:03 ( Edited 2024-04-16 06:18)

Congrats for making it! I gave up porting my Win95 maze or my experiments, I had really poor performance with those and the code was clunky to port, so I'm happy that someone else did it!

I guess that we're paying the price of the CPU lacking the power boost that the drawing operations got, we got more than three times the amount of rays to cast in the map, it's quite expensive. I guess that if you want to get into optimizing it you might want to look into BSPs or at least having a way to declare rooms so your caster can jump from a side to another without intermediate steps.

I'd like to focus a bit on your performance experiments and I have a few questions:

  • When comparing sin and math.sin (or similar functions), did you try comparing while making math.sin a local or a global (local msine = math.sin). I guess a part of the cost might be indexing the math module, but I'm not fully sure and I can't test it right now.
  • There's no tan function? Not even math.tan?
  • How much val^.5 costs compared to sqrt(val) or math.sqrt(val)
P#146446 2024-04-12 09:36

Thanks for the tips @Eiyeron.

  • Aliasing math.sin and math.cos increases their performance to the point where it's identical to the built-in commands, so that's good to know.
  • Also, val^.5 is by far the fastest method. Over 3x faster than the built-in sqrt(). So definitely a good tip for the future.
  • Apologies, math.tan does exist. But no built-in tan. So I decided to stick with the built-ins and avoid converting to radians.

At some point I'll refactor it with aliased math.* functions and convert to radians and see if that gains me any performance. Although I think it'll be minimal.
I have some map generating code in Javascript where I can control room size, so that might at least allow it to run at 30fps continuously if there are no large open areas. I have it on my list to also convert that over.
And thanks for the tip on BSP's, but I think that's a bit beyond my expertise at the moment until I can research it further.

P#146451 2024-04-12 12:48 ( Edited 2024-04-12 12:58)
1

Took the good old raycaster DDA I am using and got some interresting perf - down to 0.300 for the start position.
Still too slow to be 60fps (and still 1/3 resolution).

Main issue is that you are calling too many functions: flr, dda is almost doing a double raycasting
Also converted map to a sprite to reduce the overlay cost.

Good luck with this codebase!

Cart #nawenigowu-0 | 2024-04-12 | Embed ▽ | License: CC4-BY-NC-SA
1

P#146477 2024-04-12 18:44 ( Edited 2024-04-12 18:45)

Thanks #freds72, this is way more efficient than my raycasting code, and quite a bit shorter also. I'll have a good look through it so that I can understand where the optimisations are.

Good call on converting the 2D overlay to a sprite. I had considered that, but the cost of drawing it was so minimal compared to the losses in the raycasting that I hadn't bothered. Cheers.

P#146511 2024-04-13 01:30
1

@freds72, I used a number of your optimisations that you suggested and managed to get my code running a lot faster now. I removed any excess flr()'s and then swapped the remaining ones for \1. Also removed any reference to mid() and some superfluous calculations which weren't required.

My original code now runs 3-4 times faster, netting 60fps most of the time. Stil not quite as fast as yours, but I didn't want to take your code verbatim as I don't like to do that unless I truly understand it. And I don't quite yet.

This was a really good exercise for me to understand some of the idiosyncrasies of pico-* coding and a great lesson on optimisation. So thanks again for the pointers.

P#146602 2024-04-14 09:37

[Please log in to post a comment]