Dev Blog | Comet

In high school I spent countless hours working on games in Game Maker and RPG maker while listening to bad music. In my early 20s I made a Winnitron arcade machine and ran a game jam here in Christchurch NZ.
But In that time I never really touched any code.

Like most of us here, the playdate really captured my imagination when I saw it. And then when they talked about this small game creation tool called Pulp, something bubbled up inside me that made me want to make something. Even if it was bad and nobody saw it.

As time went on, I lurked in discord and saved little notes on my phone when I found something useful or got an idea. Wondering what my game would be about.
At first I thought I would just make my office, then my house. Outside of that? Who knows.
And tell a short story. But what would I use the crank for?

A Short Hike was one of my favourite games in 2019 so I decided to keep an eye on what adamgryu was doing next.
Last year he put out a little jam game called Spooky House.
spook
When I saw this I thought about the playdate. I liked how you could see the details of objects in the light and silhouettes in the darkness. And thought perhaps the crank could control the light.
This is my first idea.

At some point along the way, I told myself that I would be a cool challenge to learn a bit of code.
Although I've worked in tech for 11 years, it's not been something I've had interest in getting into. Now I'm a BA so and it's an even more useful skill to know. And I'd need it to make something great in pulp.

So I'm away for the weekend camping with my wife and the books on my eReader weren't working for some reason so I check twitter only to find that Pulp is out! So I read everything in the docs, and what people were talking about in discord.

Then got home and loaded up pulp. I played around with rooms and tinkled a little bit.
When trying to tackle my ring of light problem, my first idea was that each tile would have a frame that represented what point of the circle it was. Then I would make pulp read the tiles around me and tell them to move to the correct frame.

So I tried to start scripting but found I really didn't know where to start. I felt like I could step though the code in my head or speak it out loud to someone but I when I typed it out, the syntax was all wrong.
I assumed there were some fundamentals I didn't know. Or I didn't know what I didn't know.

Pulp on discord was helpful but I didn't want to bug them for every broken line.
I asked if someone would be willing to buddy up with me and someone was more than kind enough to say yes. Between my buddy, an IRL friend and SquidGodDev on Youtube, I was able to get the basics down and get moving on my game.

To jump to where I am now in the development progress, I've moved away from the frame based light circle. I forget why but it had issues.

I'm now using draw to do it. Pulp doesn't have layers except it kind of does. Both draw and fill show up above the room tiles.

I tried to make the ring of light work using Fill
Untitled
I used draw to paint the orange area around the player and used fill for the circle.
With fill, you can fake transparency.

So after my friend helped me get the crank light radius working by using loops (Something I'm trying to learn about now) and I made fill circles that looked like this....
image

I had a working POC!
Light Game Circle
And it looked great!

Unfortunately, I flew too close to the sun. It's far too taxing for pulp on real hardware.

So I've just gone back to using draw. It isn't nearly as nice but it was worth a try.
I've got other fun ideas for fill coming up tho.

There's a few things going on in that gif like the crank loop and lamp levels / animation that I'll break down at some other time.

Coming up next will be working on screen transitions between light and dark areas and hashing out story ideas with the wife.

I wanna shout out the discord once again. Lots of cool people in Pulp & #art.

Animation1
Animation2

24 Likes

I love the mechanic of the crank "recharging" the light source and increasing the size of the illuminated area. Reminds me a bit of the Metro 2033 games. Can't wait to see more!

5 Likes

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...

  1. How i'm making the ring of light
  2. How I draw the lantern and make it animate when I walk.
  3. My fade up from black screen transition effect
  4. And a small one, talking about camera follow.

Animation1

~~ 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 :pensive:.

I had made a different thread 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 drawing 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)

Then have the next page spit out all the draw commands.
Animation2

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...

Draw Code
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 :clap::clap::clap: (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 ~~

Animation3

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.
Lamp-Sheet
Lamplight0-table-8-8 Lamplight1-table-8-8

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
  • t1 - t15 for each tile
  • 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 ~~
Animation4

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.
Animation5

~~ Camera changing focus ~~
Animation7

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.

Animation6

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!

14 Likes

My game is now called Comet

My wife and I had a great little brainstorming session which actually give me a dirrection to start developing the game.
We have an idea for our characters and the basic plot which will drive the player to head off.
Let hope the scope is small enough to allow me to finish the game this year.

~~ RING OF LIGHT V3 ~~

Also, after hitting my head against the wall for too long trying to get this mechanic working, I called in the big guns.
@Drew-Lo!
Drew was polishing up his Dr Panic game, so I thought he might have a moment to help me.
He said he was happy to help!
I asked if he could create a system that changes tile frames whenever they're in the light.
But as we chatted, I realised that you then couldn't animate them. So maybe a system that uses swap might be better.

Well he went and did both!

Drew explains how he does it in his own tread: Implementation of limited sight mechanic in pulp

The short version is we use frame manipulation for static world tiles and sprites for tiles which are animate.

This new system allows outlines of objects which lets me create an effect which is closer to my original vision like Spooky House.

Animation

Now on to making the town and looking at character art.

13 Likes

I will mention that the card looks really good, but the trail on the comet is a little too rectangular, maybe it could curve a little? That and you might wanna put some text on it as the Playdate won't show a label in the menu.

Glad you got the lighting ring to run smoothly though, I'm very excited to see where this project goes!

1 Like

Hey @Benpai

Those are both great points, It's a pain to set these up so I'll wait until I'm a bit further down the line to redraw the whole thing.

1 Like

Hey, long time!

I've been taking a bit of a dev break to let life catch up a little. (And help with the playdate wiki's)
Before then, I shared some screenshots with @6AT0 to feature in his awesome Playdate Zine.

Now that it's out, I want to share it with everyone else!


Gif

I can't wait to use the new transparitcy options that @shaun added to pulp!

.

6 Likes

Looking really good - especially love the lighthouse art, and the fishing rod line tension, and of course the dialogue box complete with character portrait (it looks like you are missing an apostrophe character though :wink: )

As an idea you might animate the tiles that touch the water, like the bottom of the lighthouse and the pier pillars, to have the water line move up and down over them. Maybe you've already considered this!

Thanks for the link to the zine too!

That was certainly an idea! But after animating 39 different wave tiles to make sure they look normal around the lighthouse and cliff, I thought I'd take a break. :sweat_smile:

I might come back to it.

I just wanted to update people to know that Comet hasn't been abandoned, I just took a break.

Hope to get back into it soon :grin:

I was lucky enough to be asked to share some clips of Comet for the latest Playdate update.

I wanted to polish it up before I did and so I asked in discord if someone could help me fix up my sprites.

@stepepson put his hand up and did an awesome job.
Then he wanted to animate them.
Then he wrote his own animation system!
Then we decided to formalise our collaboration and develop Comet together!
It was a very busy week and much of the little touches in the game wouldn't look as good as they do without him.

Which is why you see his name credited in the clip.

Here is a longer sneak peek to enjoy.

And you can expect Steph to post a little how to on our animation system.

4 Likes

@stepepson has been doing amazing work updating the art.

We have a new portrait for Stella and some some really nice looks dialogue boxes.
FishermanChat1

And @Drew-Lo has been instrumental in updating the lighting code.
I made it so you can drop the lamp and Drew was then able to attach the light to the lamp.
Tech preview2

I think it makes the world feel more alive. Like you can touch it.
We hope to use this for puzzles in the future.

4 Likes

Love that leaving of the lamp, neat idea!

1 Like

That lamp is definitely selling it for me :heart_eyes:

oh man - oh man - i can't wait to play this!

My interview and hangout with @GantProdux on this week’s Tiny Yellow Machine was a lot of fun.

I got to talk about my early love and involvement in indie games, building a Winnitron, getting into the Netrunner community and finally working on Comet.

Check it out!

Tried to start it at an interesting spot

4 Likes

This was good stuff! Worth a watch.

I've mostly been fixing up bug lately and it's been REALLY fun!

Two main themes

  1. Splitting up the state of the player so I can turn player sprite elements off and on at different times.
    Update bugs

  2. Implementing my own metadata system
    Having a sprite that is two tiles means you run into a lot of edge cases where things look wrong. When you want to hide the player so it looks like they are behind a wall.

And then i'm wanting to place down a lamp in the world. But I don't want to place it if...
~the light level is 0 (Then the player can loose it)
~if the location you're placing it is solid
~if the location you're placing the lamp is "behind" something (As it would replace the tile)

The only meta data tiles have is solid or not. You can't define a tile as above.

I was planning to simply list many if statements for each "above" tile but @orkn had a better idea. So all credit to him on this one.

Orkn's Metadata system

"So if the solid function didn't exist, and we wanted some way of knowing if a tile is "solid" or not, and we wanted to check if a tile is solid or not in multiple places around our project, we might decide to make our own version of the solid function. In the game script we add:

on isTileSolid do
  if tile=="white" then
    tileIsSolid = 0
  elseif tile=="black" then
    tileIsSolid = 1
  end
end

and we can keep adding elseifs for every tile in our game. (As a potential optimisation we realise we could start the event with tileIsSolid = 0 and then only add ifs for those tiles that are solid, but maybe we appreciate the explicit list of all tiles as it makes changing the metadata easier, idk!) Then wherever in our code we want to check if a tile is solid, we do this:

tile = name x,y
tell event.game to
  call "isTileSolid"
end
myTileIsSolid = tileIsSolid

and that is functionally equivalent to the actually built-in

myTileIsSolid = solid x,y

The isTileSolid event is effectively a function with an input tile and an output tileIsSolid , it's just Pulpscript doesn't support event arguments and return values so we just have to be careful juggling global variables. The solid function does exist, so this isn't necessary, but if we want to add new kinds of "metadata" to tiles, like their "aboveness" in this case, then you could take this approach."

I took that and made the following.
In game I have

on isTileAbove do
	tileIsAbove = "false"
	if aboveTarget=="roof top" then
		tileIsAbove = "true"
	elseif tile=="other" then
		// tileIsAbove = "true"
	end
end

And in my on cancel event which I use tileIsAbove for checking if the player can place the lamp

Above

Place lamp code
on cancel do
	
	if lampHeld=="true" then // holding the lamp | There's zero lamps on the screen
		lampTargetX = event.px
		lampTargetY = event.py
		
		if playerLastMove=="L" then
			lampTargetX--
		elseif playerLastMove=="R" then
			lampTargetX++
		elseif playerLastMove=="U" then
			lampTargetY--
		elseif playerLastMove=="D" then
			lampTargetY++
		end
		
		lampTarget = name lampTargetX,lampTargetY
		lampTargetSolid = solid lampTargetX,lampTargetY
		aboveTarget = lampTarget
		tell event.game to
			call "isTileAbove"
		end
		
		if lightRadius>0 then
			if lampTargetSolid==0 then
				if tileIsAbove=="false" then
					tell lampTargetX,lampTargetY to
						swap "Lamp"
						lampHeld = "false" // not holding the lamp
						lampRoom = event.room
						
						if illumination=="dark" then
							// re-illuminate the room so that the radius now centers around the lamp instead of the player
							emit "darken_tile"
							tell event.game to
								call "darken_room"
								call "illuminate_tiles"
							end
						end
						
					end
				elseif tileIsAbove=="true" then
				  sound "prohibited"
					say "Can't Place lamp behind things" at 1,10,21,3
				end
			elseif lampTargetSolid==1 then
				sound "prohibited"
			end
		elseif lightRadius==0 then
			sound "prohibited"
			say "I don't want to lose this lamp in the darkness" at 1,10,21,3
		end
	end
end
3 Likes

Been doing lots of bug fixes since I last updated everyone.

Two big changes tho.

The first is a static light system.
This was needed for the start of the game before the player has a lamp.
The second is a new pretty light system where we round the corner off on the lamp.

That raised the challenge of the pretty static corners and the player's pretty lamp corners overlapping.
Something like this...

Well with the help of @Drew-Lo we were able to make a system that worked!
playdate-20220627-204721

2 Likes

The heartrate: XX is supposed to trigger a game-over if it gets too high? Maybe make it only count up from 60? With heartrate: 00 you'll be just as dead as you would be with heartrate: 300 :wink:

1 Like