What a great idea! I was wondering how do to something like this.
A couple of techier (for Pulp) bits I thought might be interesting to share:
Implementing simple stealth
I wanted to make some simple line-of-sight stealth mechanics - think being spotted by Pokemon trainers. I thought through a few different ways I might achieve it.
The naive approach would be to loop over each guard (or whoever is trying to spot the player) every frame and then look out along their line-of-sight until you find the player, hit a solid wall or reach the edge of the screen. With a bit of code this could work but it'd mean adding code to (or called from) the game's loop event and it could be quite impactful if I have several guards on screen and am needing to check each guard's line of sight every frame.
My first "trick" was to flip around the way of thinking about line-of-sight and instead start from the player. That means only worrying about four lines-of-sight in the cardinal directions from the player.
My second trick was to decide to use chained events to propagate along and check the lines-of-sight rather than having some code do it from the loop event. Every frame I already call an event on the tile at the player's location called "playerPresent" (I use it for hiding the player and taking damage on spike tiles). I realised I could add a "playerPresent" event handler to my floor tiles that would trigger four events, one for each cardinal direction, that would call the same directional event on the next tile in that direction. Provided the next tile in that direction similarly implements that directional event the event will propagate outwards along that line of sight. If it reaches a guard, the event will be called on the guard, and I can then check if the guard is facing in the right direction (from their current frame) and have them react accordingly. Even better, when laying out rooms I only need to use floor tiles implementing these events if they will potentially be in a guard's line of sight (so while I could have the events on every floor tile, it's cheaper not to). All the code ends up contained in the relevant tiles and there is zero overhead otherwise as I already had the playerPresent event.
That implementation looks a little like this (the "v" tiles are placeholder floor tiles implementing the directional events):
One neat advantage of this approach is that doing something like pushing a block into the guard's line-of-sight will block their sight without me coding for that specifically!
Randomising on which tile something happens from a set of tiles
Or in other words - whack-a-mole!
This mole enemy will popup from a random mole hole and throw a stone in the cardinal direction closest to the player.
I wanted to randomise which hole the mole would pop up from but without hardcoding the locations of all of the mole holes on the screen, and also without using loads of variables to store the number of holes and their locations.
After a bit of thinking, the solution suddenly occurred to me using only one variable. When I want to spawn a mole I first set this variable to be 0. I then emit a "spawnable" event. The mole hole tile implements this event to simply increment the variable, so when the emit completes I have a count of the spawnable tiles. I then reassign the variable using the
random function to get a number between 1 and the variable itself i.e. the count of spawnable tiles. This is effectively the index of the list of spawnable tiles where I want to spawn the mole. I emit a second event, "spawn", again implemented by the mole holes. This event checks the variable - if it is greater than 1, it decrements it and finishes. If it is equal to 1, it spawns the mole and then decrements. If it is equal to 0 it does nothing. You can think of emit as looping through a list, and my randomised variable decides at which index in the list something should happen.
Now I can add, remove and move mole holes around a room and know that the spawning will always be randomised correctly, no further effort required!
Here's how that line-of-sight stealth is shaping up in-game:
The screen wipe transitions are implemented using
crop. Unlike other approaches,
crop doesn't have to be called from the player's
draw event, so I could keep the code for the screen transitions all self-contained.
Hey, curious if I can use this demo code in a zelda-like project I'm currently working on. Would be sure to include your name in the credits and identify your portions of code in the comments of my own
Sure, go ahead and use the movement code from the demo, I'm glad to be of help! Just please don't use the player tiles/character art itself
It's been a while again since I've posted anything here, but I have been steadily working away on Resonant Tale. The big news is that I now have a collaborator for music and sound! My own efforts at them (even with Pulp's simple interface) proved woefully inadequate so I'm pretty excited. Good sound really is transformative! It's great being able to get an insight into that side of things by working with someone who knows their stuff
On the subject of smooth movement, my approach here in some ways already feels outdated since people have found
draw can accept non-integers, which can free you from the grid constraints entirely (with a bit of effort). I'm already tied in to my approach, which is fine as it works for what I want it to do, and the last thing I did was extend it so that the player can be smoothly animated while descending a ladder - like this:
(While on a ladder you also can't turn to face left or right, and you can't place bombs or shoot arrows.)
Absolutely, thank you! And of course not- I'm doing all my own art
Over the last couple of days I revisited my tile-based pulp platformer prototype to shape it into a little "arcade cabinet" for inclusion in another soon-to-be-revealed project I was approached about.
My goal was to:
- Make a proper level
- Crop the screen to an arcade cabinet feeling aspect ratio
- Implement coins to collect with a saved high score
- Implement a timer with a saved high score
- Give the game some polish with little things like a title screen
- Refactor all of it to be self-contained and as easy as possible to drop into a larger pulp project!
Here is what it looks like right now:
The code is simple, but it's a lot of effort!
Coins are an item tile with this basic script:
on collect do
on pickup do
tell event.room to
collect is overriden to do nothing because of the non-standard player movement. Instead every frame I call the
pickup event on the player's location.
The implementation of screen scroll (by having overlapping rooms with inset edge exits stitching them together) means that the "same" coin will be placed in multiple rooms. When the player picks up a coin in the level it not only needs to be swapped out of the current room, but any other room in which it is visible. For this reason every coin has its own variable to track whether it has been collected, and when picking up a coin the room script has a matching event to set the correct coin variable:
on pickupCoin2x10y do
thill_coin_1 = 1
There is an event like this for every coin that can be picked up in that room.
When entering a room the coin variable needs to be checked to see whether each coin visible in the room should be swapped in or out. Swapping in as well as out means the level can easily be reset without restarting the entire game:
on enter do
if thill_coin_1==0 then
thill_tile = "thill coin"
thill_tile = "white"
tell 2,10 to
That's it for the concept - but multiply up for every coin (each appearing in multiple rooms) and it's a lot of code and a lot of copy/paste while changing the numbers!
I refactored extensively to try and make it as easy as possible to just drop in to another project:
"Portable" means "small". The game uses 11 rooms (10 for the level and 1 for the title screen). In addition to the default black/white world tiles the game only uses 10 player tiles, 11 sprite tiles, and 4 item tiles (cut down from the original prototype).
Every variable starts with
thill_so there shouldn't be any conflicts despite pulp having only global variables.
All of the rooms and tiles similarly start with
thillso they will be grouped together in the pulp editor's alphabetically sorted lists
All of the code is self-contained within tile and room scripts, primarily a
tile hill codetile (that doubles up as the coin HUD icon, just to save on tiles!). The player and game scripts have the minimum hooks to relay the required pulp events to the Tile Hill code. Essentially to start Tile Hill from within another pulp game someone just needs to call the one
bootevent to start the game and ensure those events are relayed for the duration (probably just with a flag that is set when the game is running).
Pressing "B" while playing will return the player to the title screen. Pressing "B" on the title screen will call a
quitevent which is intended to quit the player out of Tile Hill and back to the containing pulp game. This requires a little bit of tidying of
configchanges and resetting the screen crop.
Hopefully that's mildly interesting!
ART& was just released on catalog - check it out and head to the in game arcade if you want to give Tile Hill a play. It's very cool to have contributed something to a catalog release!
I still think what you did with it is magic
Resonant Tale is finally approaching a complete game, and while it's not quite finished yet, yesterday I spent a surprisingly short amount of time trying to make the game more accessible. I thought my experience might be interesting to share
Previously I have implemented some cheat codes in Resonant Tale that can be entered on the title screen, partly for fun and partly for making testing easier. One of those cheats was player invincibility, but it occured to me this would really be better as an easily discoverable option in some assistance settings. That prompted me to consider accessibility in the game in general.
I was a bit trepidatious about exploring accessibility. It's not something I'm familiar with or have consciously addressed before and I was worried about how much work the additional functionality would be, especially using Pulp. Obvious accessibility features that sprung to mind having seem them in other games, like adjustable font sizes, are not readily supported by Pulp (which I think is a fair trade off with Pulp's intended simplicity as a starting point into game development). Nethertheless I started reading up on Accessible Player Experiences and Game Accessibility Guidelines, both great resources. I geared my mindset to thinking about what can be easily done rather than getting hung up on what can't!
More details below, but this is the assist menu as it now looks in game:
Up until now my approach to save data has been really simple - if the player selects Continue from the title screen I use
restore while if the player selects New Game I use
toss. With assistance settings however I want them to persist even if a new game is started. I replaced my
toss call with a custom game event (also called "toss") that looks like this:
on toss do
// Re-store persistent vars
This works great and is going to be useful beyond these assist settings as I'm planning on keeping a record of whether the game has ever been beaten so I can offer a New Game Plus!
I already had a version of this implemented in my invincibility cheat so this was easy. From an accessibility perspective being able to turn off player damage is a maybe heavy handed but simple solution to a host of potential issues.
I already had all player damage implemented through a player event
takeDamage so this was as easy as adding this one conditional to the start of the event handler:
on takeDamage do
if assist_damage==1 then
// code handling player damage
I'd describe Resonant Tale as "low combat" - there are numerous hazards but few traditional enemies. Bosses are the exception as tests of player execution and as barriers to progress. While a valuable aspect of the game, making them optional prevents them blocking someone from enjoying the rest of the game which is not as challenging or demanding of execution.
Again I already had logic in place for triggering bosses, previously there to prevent bosses from being fought multiple times if returning to the boss room for example. This made implementing a simple skip almost trivial as (for the most part) I only needed to alter a few conditionals. The first dungeon's boss, for example, has a conditional changed from this
on enter do
if has_bell_of_vitality==1 then
// code toggling the boss
on enter do
flags = has_bell_of_vitality
flags += assist_bosses
if flags>=1 then
// code toggling the boss
flags>=1 pattern is a pulpscript version of an
Input, especially analogue input, is an obvious consideration for accessibility. Resonant Tale has minimal use of the crank, used only in a couple of instances, only one of which is required to beat the game. A setting to remove those instances to prevent a small aspect of the game stopping someone from playing it entirely seems an obvious win!
With the assist option enabled, for an optional puzzle I simplify the puzzle to no longer require the crank. For the required puzzle simplifying it was not an option so I just made it pre-solved (although I may revisit this to make an alternative solution instead).
Timing puzzles refers to anything (outside of bosses) where precise timing is required from the player. This covers a relatively significant minority of Resonant Tale and was the most work to implement as I took different bespoke approaches depending on what content I was changing.
Take this room as an example - it has retracting spikes that the player must time passing over to not take damage:
With the assist option enabled I replace the retracting spikes with static spikes and add a path through for the player to navigate:
Not every timing puzzle has quite as nice a replacement as this and in multiple places I simply removed the challenge entirely, but I like being able to offer an alternative like this where I can!
Really excited that a reveal trailer for Resonant Tale just featured in the Playdate Community Direct! Here it is if you missed it
This looks wonderful! Amazing it's in Pulp too!
Something completely different for a change, a little toy called Paint with Pulp!
Drawing at double the room tile resolution! I made 16 sprite tiles that represent all permutations of a 4 sub-tile grid, and these get swapped in as you draw.
The player is hidden (trapped in the middle of the room) and instead I draw the cursor using
fill , with the position updated through the sprite interact event that's mimicked across the tiles.
Anyway, at the moment a lot of games have a nice launcher card but the default list view icon, so I decided I wanted to make a joke game which has a nice list view icon but where the launcher card looks like a card version of the default icon. I think it came out pretty well (read: terrible)!
I added a second canvas to draw on, you can switch between them by cranking - this allows for some very basic animation. Also note that the canvas is saved (I just reused my room saving code from Pulpino )
If you want to try it out (and ruin the look of your card view launcher), here's the pdx:
Paint-with-Pulp!.pdx.zip (60.0 KB)
I realise I haven't posted about it here, but Resonant Tale is coming to Catalog! It launches September 12th on both Catalog and Itch, which is almost exactly a month away, so please look forward to that!
Anyway, I actually came here to share a concept called Travelling Balloon:
The player controls a hot air balloon that moves automatically with the wind, but the crank moves the balloon between different air layers with different wind directions (see HUD on right). The top layer is the prevailing wind for that room; the middle and lower layers vary by tile (which means 16 permutations for every tile to cover all wind direction combinations). The little arrows on the tiles are for ease of concepting only - the intention is that you'd only see the wind direction on the HUD and would have to learn the hidden wind patterns across the map.
One of the "tricks" here is that every map tile is a sprite, not a world tile. For one the tiles being solid means the dpad won't trigger any movement without me having to
ignore input, and second it means each map tile can define its wind directions in its script.
So many end lines.
How about calling it a "stairway to heaven", or maybe a "pass" because when you look at it, you want to look away.
Personally I would have made the player automatically step off the warp tile right after warping, instead of continuing to spin around after warping and save myself from some grief, but we all do things differently and there's no "right way" when making your own game.
Keep up the great work!
I've built out more of Travelling Balloon, but no more to share yet. What I do have to share is this unrelated prototype I've put together tonight:
- A tileset made up of 16x16 meta-tiles
- A 16x16 player drawn using the decimal draw trick for off-grid movement
- A solid 20fps on device!
The black border along the bottom and right hand side is because of the odd number of 8x8 tiles across the screen. How I'll "hide" that depends on what I decide to make, but I guess it'll be in a HUD of some kind!
(This is 100% "why not just use the SDK" territory here... but I'm having fun )
Today is (finally) the release day of Resonant Tale, available now on Catalog and Itch! A massive thank you to everyone who has been following the game through this thread, whenever my motivation has waned it's been the support of all the lovely people in the Playdate community that has kept me going. Much love to you all and here is a final something to share from the game!
Congratulations on your release! Hope everyone with a Playdate plays it!
Thanks Grim! Appreciate it!