Completing two Pulp tutorials
Tags: gamedev, beginners, playdate, pulp
Published: 2023-01-25 14:43:55

Playdate's developer site has a [list of links](https://play.date/dev/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:
{% embed https://www.youtube.com/watch?v=r5GnCjsAqeQ %}

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:
{% embed https://www.youtube.com/watch?v=p4eLcP3hgJc %}

### 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 new `World` tile called `wall`
- Update the pixels to match what the author drew
- Toggle the option to make this a `wall` tile
- Paint the room with both `white` and `wall` tiles such that all tiles are white except the border tiles, which are `wall` tiles
- Delete the only exit tile in the current room
- Create a new `Sprite` tile called `gate`
- 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 the `wall` tile
- Paint an enclosed area in the room using the three `World` tiles
- Create a new `Item` tile called `key`
- Update the pixels to match what the author drew
- Add a Behavior script to the gate sprite
- Go to the `Script` mode and select `gate`
- Write an `on interact do` event listener that checks the value of `keys` 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 called `keysicon`
- Update the pixels to match what the author drew
- Toggle the option to make this a `wall` tile
- Go to the `Script` mode and select `player`
- Write an `on draw do` event listener that updates the top-right tiles such that the left-most tile is the `keysicon` 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 called `spikeOut`
- Update the pixels to match what the author drew
- Create a new `Item` tile called `spikeIn`
- Update the pixels to match what the author drew
- Go to the `Script` mode and the `game` 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 the `loop` 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 the `spikeOut` 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 as `damage` - 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` by `damage`
- Also in the Player script, write an `on update do` event listener that `emit`s - thereby calling some function on every tile that listens for that function - a custom function called `playerUpdated`
- 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 the `spikeIn` 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's `x` and `y` 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!