Robert Mion
Posted on January 25, 2023
Playdate's developer site has a list of links on several topics.
One such section features links to tutorials on Pulp.
Three are youtube videos, two of which are by user SquidGodDev.
The rest are articles on Playdate's developer forum.
I intend to work my way top to bottom.
In this article, I work through both of SquidGodDev's tutorials.
PulpScript: A Beginner's Guide
This is the first video I will watch and code along with in Pulp:
Sections in the video:
- Scripts
- Event handlers
- Variables
- Conditionals
- Functions
- Event variable
PulpScript snippets:
on any do
if keys==1 then
// do something
end
end
on enter do
say "You entered a new room!"
end
on load do
health = 10
maxHealth = 15
end
on draw do
label "Health: {health}" at 0,0
end
on interact do
health += 1
health++
health--
if health > maxHealth then
health = maxHealth
end
if health <= 0 then
health = 0
fin "You died..."
end
end
on collect do
teleportX = event.x
teleportY = event.y
teleportX += 5
goto teleportX,teleportY
sound "teleport"
end
Impressions
- This video - like many other tutorials on Youtube - was cut together to present information at a rapid pace
- That made it difficult to fully absorb all of what was being taught without having to pause and rewind
- I learned how swapping tiles is pertinent
- I learned how easy it is to place text on the screen
I'm excited to watch the next video and make a simple game
Make an Adventure Game for the Playdate with Pulp
This is the second video I'll watch and code along with in Pulp:
Intro
- The first 25 seconds make me very excited to follow along and complete this whole tutorial
- I imagine I'll be doing a lot of pausing and rewinding, given this editor's tendency for sharp cuts
Making a Door
- From
Room
mode, make a newWorld
tile calledwall
- Update the pixels to match what the author drew
- Toggle the option to make this a
wall
tile - Paint the room with both
white
andwall
tiles such that all tiles are white except the border tiles, which arewall
tiles - Delete the only exit tile in the current room
- Create a new
Sprite
tile calledgate
- Update the pixels to match what the author drew
- Add the sprite to the room at the indicated position
- Create two additional
World
tiles that are each slight variants on thewall
tile - Paint an enclosed area in the room using the three
World
tiles - Create a new
Item
tile calledkey
- Update the pixels to match what the author drew
- Add a Behavior script to the gate sprite
- Go to the
Script
mode and selectgate
- Write an
on interact do
event listener that checks the value ofkeys
and performs a tile swap if it is 1 or alerts the player to collect a necessary key otherwise
Displaying the number of keys
- Create a new
World
tile calledkeysicon
- Update the pixels to match what the author drew
- Toggle the option to make this a
wall
tile - Go to the
Script
mode and selectplayer
- Write an
on draw do
event listener that updates the top-right tiles such that the left-most tile is thekeysicon
tile and the right-most tile is black and then the amount of keys collected by the player
Health system
- Write an
on load do
event listener in the player script, setting max health and health variables - Create three
Sprite
tiles for each of the heart UI states: full, half, empty
Drawing the hearts in the UI
Clever: a loop that draws one heart for every two health.
There's some counting by 2 and by 1 to account for number of hearts and placement in the UI.
- Initialize two variables to track current heart and static Y position
- Store the desired tile name in a variable
- Set the current heart to half of the total possible hearts
- Swap the tile at the desired position with the desired tile
- Update the current heart variable
Draw the correct hearts in the UI
Some additional conditionals are needed to account for the half-hearts.
This took a few pause-rewind-think-play loops for me, but I think I get it now.
- Set a variable equal to the current health
- Check whether the variable is greater than the current heart being drawn
- If it does, decrement it by one, then check if it is now equal to the current heart
- If it is, queue up the half heart
- Otherwise, queue up the full heart
- If it wasn't greater to begin with, proceed with the empty heart
Spikes
Form and Function
- Create a new
Item
tile calledspikeOut
- Update the pixels to match what the author drew
- Create a new
Item
tile calledspikeIn
- Update the pixels to match what the author drew
- Go to the
Script
mode and thegame
script - Delete the contents of the script
- Write an
on loop do
event listener that emits a yet-to-be-written function each time a certain amount of frames have passed - The dependencies of the conditional are:
event.frames
, an interval, a division and rounding operation, and a check for equality - The interval is then separated into an
on load do
event listener and used in theloop
event listener - Set the behaviors for both spike tiles to script
- In each script write a custom
on spikeUpdate do
event listener that swaps one tile for the other
Spike Damage
- Amend the
spikeUpdate
function in thespikeOut
script - Add nested conditionals to check whether the player is on a
spikeOut
tile - Decrement
health
by 1
But wait...there's a better, far more complicated approach?
Better writing it, I notice that health is only decremented when the tiles are swapped from spikeIn
to spikeOut
.
If the player moves onto a spikeOut
tile, no damage is dealt.
Turns out that the better, more complicated approach accounts for this mistake:
- In the script for the recessed, non-damaging spike tile
- Instead of decrementing health by one...
- A copy of
spikeDamage
is made - saved asdamage
- as a way to future-proof this algorithm for other items that may damage the player - A yet-to-be-written custom event called
damagePlayer
is called only on the Player - That event just decrements
health
bydamage
- Also in the Player script, write an
on update do
event listener thatemit
s - thereby calling some function on every tile that listens for that function - a custom function calledplayerUpdated
- In the script for the raised, damaging spike tile, that event is written and inside it is the exact same code inside the
spikeUpdated
function in thespikeIn
tile
The way all of this seems to work is:
- Whenever the non-damaging spike tile updates...
- Check if the player is on that type of spike tile...
- And decrement health by the value stored in
damage
, because it just changed to the damaging spike tile - Also, whenever the player moves...
- Check if the player is on a damaging spike tile...
- And decrement health by the value stored in
damage
I think I understand it all.
Though I'm not confident I could write it all from scratch yet.
Still, it's great that it appears to add the expected behavior to the spikes and the player and the health!
Moving Hazards
This is a long section. I wondered why. So I watched it all first.
Answer: because simulating an item moving requires
- Four tiles
- Conditionals
- Direction
- Tile swapping
- Code duplication
By the end, the items move up-down and left-right against any wall items, and unfortunately merge into a single item when two of them collide.
Time to study the code!
- Created four identical tiles, named per their direction of initial movement, and set their behavior to Script
- New variables in the
load
event to track timing and damage amount - Duplicated the code in the
loop
event, adjusting for the new interval and event emitted - In the new custom event - being written first in the downward moving spike tile - the
event
object'sx
andy
members are used to capture the position of the next tile and swap two tiles: current tile for white and next tile for current tile...simulating downward movement - Bug: the spike never moves upward!
- Use of the
solid
function helps determine the type of the next tile - Check whether the type is solid - is it 1? - and if so adjust the tile name and the position at which to swap
- Duplicate the player collision detection code, adjusting variables where necessary
- Add a
playerUpdated
function and duplicate the same player collision detection code - Duplicate all of the above code for the three other tile directions
I play tested a bit between each code addition to confirm the spikes moved as expected.
Then I added some spikes and walls and play tested to confirm the player takes damage when colliding with the spikes.
Everything appears to work!
Screen Shake
- A simple one-line update, thanks to the use of the custom player function
Sound Effects
This section is difficult to follow due to the speed of the author's video cuts and the small font size of the UI.
I'll try my best to re-create what is being setup.
Creating the sounds
-
move
: success -
hurt
: mostly a success, though I feel like my sound lasts longer -
key
: success -
doorOpen
: success -
spikeOut
: success -
spikeIn
: success -
spikeBallBounce
: success
Scripting the sounds
-
move
: success -
hurt
: mostly a success, though I feel like my sound lasts longer -
key
: success -
doorOpen
: success -
spikeOut
: success -
spikeIn
: success -
spikeBallBounce
: success x4
Play testing confirms the sounds work as expected!
Final Touches
Exits
- First I tried a one-way connection
- Then connecting rooms
I realized I needed another key to open the gate to the new room.
I put the key in a spot that a spike would interact with.
This made the key disappear.
I tried troubleshooting:
- Adding a conditional in the key's
collect
event handler that made it disappear only when the player interacts with it
Sadly, the spike overrides this by swapping the tile to which it is adjacent.
For now, this is still an issue: I can't place a key in the line of a spike.
Fin
- I added a Fin exit
- And created a flag to signify it
- Then, added a health-based ending for 0 health
Conclusion
- The author alludes to several fun ways to improve upon the game
- All of them seem like great exercises to further practice what I learned here
This video tutorial turned out to be a fantastic exercise in applying much of what I learned by reading the Docs and inspecting other games' code.
Other tutorials, here I come!
Posted on January 25, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.