Posted April 12, 2023 by guv_bubbs
#Pulp Development
My plan for my second post was to catch everyone up with what I've already done so going forward, I can just start talk about the new things i've been working on but the performance issues in my game were bigger than I realised. I have managed to squeeze in one visual flourish since the last post tho.
In this post I will explain 4 features...
~~ RING OF LIGHT V2 ~~
So I was fully expecting the fill
circles to be too much but had assumed If used draw
, it would be okay.
No luck. ๐
I had made a different thread on the forums where I talk though the issues. It's tough when the web player doesn't perform the same way as real hardware does so I was limited in how much I could test, needing to relying on kind people like @neven and @pichuscute from rngparty.com.
In short, i'm draw
ing far too much on the screen and the neat little loop...
// This whole section controls the light around the player if lightRadius<0 then lightRadius = 0 elseif lightRadius>lightRadiusMax then lightRadius = lightRadiusMax end redraw_x = 0 while redraw_x<screen_width do redraw_y = 0 while redraw_y<screen_height do check_x = centre_x check_x -= lightRadius if redraw_x<check_x then draw "black" at redraw_x,redraw_y end check_x = centre_x check_x += lightRadius if redraw_x>check_x then draw "black" at redraw_x,redraw_y end check_y = centre_y check_y -= lightRadius if redraw_y<check_y then draw "black" at redraw_x,redraw_y end check_y = centre_y check_y += lightRadius if redraw_y>check_y then draw "black" at redraw_x,redraw_y end redraw_y++ end redraw_x++ end
pushed our poor little draw
event to it's limit.
Along side that, Shawn said that using config.follow = 1
Also incurs a performance hit as it "effectively dirties every tile every frame" which I can understand.
I was determined! So between waiting for people to test, I pushed forward in what my friend called "Meta Programming". The idea is you write second program to make your first program.
I decided to use my years of experience in the Google Sheets framework to create all the hard coded draw commands I needed at each light level.
I would draw out the black tiles on one page (Note the punch out for the lamp)
I then saved an output for each light level and lined them up. Eliminated blanks and sorted them such that I could workout which tiles need be be drawn at each light level.
With some if statements I ended up with something like this...
if lightRadius<=1 then // 3x3 circle draw bit of "black" in the middle end if lightRadius<=2 then // 5x5 circle draw a little more of "black" in the middle end if lightRadius<=3 then // 7x7 Circle draw a round square of "black" end if lightRadius<=4 then // 9x9 circle draw quite a lot of "black" end if lightRadius<=5 then // 11x11 grid draw HEAPS of "black" everywhere end if lightRadius<=6 then // 13x13 grid draw ALL of the "black" end if lightRadius==0 then // hide player and show outline when there's no light hide draw "bottom outline" at centre_x,centre_y draw "top outline" at centre_x,playerheady draw "!" at 12,5 draw just the missing bit of "black" end
The good news is that outside, my game plays at an impressive 8 fps๐๐๐ (Inside was fine) This is huge improvement over the... checks notes... 0 fps I was previously getting.
So I will keep looking at this. Shawn has suggested that I could get something working with the frame manipulation method. So look out for a v3 in the future.
~~ Lantern UI and Animation ~~
The idea here is just expanding on Neven's portrait example.
For his game, he had 5 or so portraits with 9 tiles each. In my game, I have 7 lamp states which are 15 tiles each. And 2 frames of animation.
HOT TIP!
Horizontal sprite sheets are nice to look at but since tiles import left to right, it's much nicer to import vertical sheets. It helps when you need to place and name them.
Yes I had to have 70 unique tile names. Google sheets once again comes in with the save.
I used the format "lamp{lightRadiusin}t1f{lampframe}"
lightRadiusin
is a variable for the light level
lampframe
is the frame of animation
Then started drawing the lamp based on my light radius and the lamp frame.
draw "lamp{lightRadiusin}t1f{lampframe}" at 1,2 draw "lamp{lightRadiusin}t2f{lampframe}" at 2,2 draw "lamp{lightRadiusin}t3f{lampframe}" at 3,2 draw "lamp{lightRadiusin}t4f{lampframe}" at 1,3 draw "lamp{lightRadiusin}t5f{lampframe}" at 2,3 draw "lamp{lightRadiusin}t6f{lampframe}" at 3,3 draw "lamp{lightRadiusin}t7f{lampframe}" at 1,4 draw "lamp{lightRadiusin}t8f{lampframe}" at 2,4 draw "lamp{lightRadiusin}t9f{lampframe}" at 3,4 draw "lamp{lightRadiusin}t10f{lampframe}" at 1,5 draw "lamp{lightRadiusin}t11f{lampframe}" at 2,5 draw "lamp{lightRadiusin}t12f{lampframe}" at 3,5 draw "lamp{lightRadiusin}t13f{lampframe}" at 1,6 draw "lamp{lightRadiusin}t14f{lampframe}" at 2,6 draw "lamp{lightRadiusin}t15f{lampframe}" at 3,6
Here By default, it's always frame 0, but when I step I move it to frame 1. This creates a bobbing effect.
on update do lampframe = 0 lampframe = 1 wait 0.3 then lampframe = 0 end
You can see this idea done really well in @Harspoon 's Samurai Game for the sword health.
~~ Fade up from black screen transition ~~
This one is simple when you talk about it. Just draw black tiles on every tile of the screen, then do it again y-1. But inner and outer loops are still a tongue twisters of the mind when I try and write them. After thinking I could do it alone, so I got some help. (Maybe I should start with a simple loop first.)
// transition effect if fadevalue!=fadetarget then if fadevalue>event.py then hide end fadex = 0 while fadex<screen_width do fadey = 0 while fadey<fadevalue do draw "black" at fadex,fadey fadey++ end fadex++ end fadevalue += fadedirerction end
From here you define a few values and you're away.
on fade_to_black do fadevalue = 0 fadetarget = screen_height fadedirerction = 1 end on fade_to_white do fadevalue = screen_height fadetarget = 0 fadedirerction = -1 end
The way this is done, you can actually made it fade any which way you want by changing the values up.
~~ Camera changing focus ~~
As we were building out the first version of the ring of light, my friend Dom asked if we should make the camera follow the player.
I thought this was a great idea as could help create a feeling of an unsafe world. "It's all around me and I can't see it." Then when you're inside, you can see everything and the world is still. Safe. (It's subtle but I think it works.)
So I make sure every room declares what time of room it is.
on enter do config.follow = 1 // 1 = camera follows the player end
And I can then use that to determine how other elements of the game work.
For example, I only fade up when entering a light room.
One fun side effect of using a cantered camera and drawing on the full screen is you can actually draw outside of the room. And even animate it.
We theory crafted for a few mins on how we could use this to show part of the next room outside of the bounds of the current room but didn't go any further with it. Perhaps that's something someone smarter than I can tackle.
Next up is still story stuff. Planning to brainstorm ideas this weekend which has a reasonable scope.
Thanks for reading!