Log In  
Follow
ericb
[ :: Read More :: ]

Cart #lofi_gymnopedies-0 | 2024-02-15 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
15

classic tunes & chill beats

created by Eric Billingsley for the PICO-8 Free Music Jam

original compositions by Erik Satie

cover art inspired by Portrait of Eric Satie at the harmonium by Santiago Rusiñol and lofi girl by Juan Pablo Machado

thanks to Packbat and Bikibird for organizing the jam and providing the sample album cart

licensed under CC4-BY-NC-SA

instructions

to use in your pico-8 game, first copy over sfx 0-7, the custom instruments (you can copy these easily from the pico-8 music tab in sfx view, though you may need to use the sfx tab to paste)

then, in the pattern view, copy over the patterns for the track(s) you need -- this should automatically copy over the needed sfx as well

implementation info

I tried to keep things relatively accurate to the score, with a few liberties taken here and there in the accompaniment and some chord notes left out where necessary

in addition to the 8 custom instrument sfx, gymnopédie no.1 uses 21 sfx slots, gymnopédie no.2 uses 18 slots, and gymnopédie no.3 uses 17 slots. Collectively they use all 64 slots, so if you need room for other sound effects then choose just one or 2.

the pieces use all 4 channels, though I've tried to arrange them in order of priority:

  • channel 1 contains mostly single chord tones and can be safely interrupted without being very noticeable
  • channel 2 contains the percussion
  • channel 3 contains the main accompaniment (bass and chord tones, usually harmonized to a 4th/5th using detune)
  • channel 4 contains the melody

when all 4 channels aren't needed, I've tried to rearrange things to keep channel 1 free

if you need a loop rather than a complete piece:

  • gymnopédie no.1 loops well in its entirety if you change sfx 28's length from 12 to 9, or just loop patterns 0-4 if you want something shorter
  • gymnopédie no.2 loops well from 19 back to 11 or from 16 to 11 for a shorter loop
  • gymnopédie no.3 loops well from 28 back to 22 or from 24 to 22 for a shorter loop

This project is also on itch.io along with exported .wav files.

P#141537 2024-02-15 20:59 ( Edited 2024-02-16 18:58)

[ :: Read More :: ]

Cart #praxis_fighter_x-2 | 2024-02-12 | Code ▽ | Embed ▽ | No License
115

Controls

  • ◀▲▼▶: Move
  • Hold (X): Fire main weapon (spreadshot)
  • Hold (O)/Z/C: Charge secondary weapon (bomb) / focus mode.
  • Enter: Pause/menu

About

Praxis Fighter X is a fast-paced single stage retro shoot-em-up. Dodge bullets and fight off the militarized police force defending a corporate toxic sludge pipeline.

Inspired by old-school shmups/STGs, the game focuses on reflex-based action and satisfying destruction while including some modern comforts like a smaller player hitbox and a focus mode.

It's on the difficult side but with dedication and the power of 3 Continues you will persevere.

Scoring

Build your scoring multiplier and keep your combo meter up by taking out enemies and ground targets. Dealing damage also helps sustain your combo meter — once it runs out, your multiplier will begin falling.

Tip 1: Your combo meter depletes more slowly while charging your bomb weapon.

Tip 2: If your bomb collides with an enemy mid-air, you get your shot back with a shorter delay

Tip 3: Collecting a powerup you already have or a 1UP when at 3 lives will give you points instead of the usual effect. Your current multiplier applies here too.

Tip 4: Your multiplier also applies during boss fights. Try to take out all the boss's destructible parts while continously dealing damage for maximum points.

Credits

Praxis Fighter X was created by Eric Billingsley.

Thanks to Krystian Majewski of Lazy Devs Academy for creating the Advanced Shmup Tutorial which inspired me to work on the game. Though I didn't use the solutions from the tutorial exactly, watching the videos was really helpful in understanding what makes shmups tick and what challenges I would need to plan for. Boghog's Shmup Workshop series was also really helpful.

Thanks to Otto, LokiStriker, Louie Chapman, Catto, Glimm, Trog, TheGacko, Jonah Davidson, and Jeep Barnett for playtesting and feedback.

Thanks to Aktane for feedback on my initially terrible bullet visibility and Heracleum for help with turret rendering tech.

And thanks Maria for your encouragement and for being there for me all along the way.

Future Plans

Praxis Fighter X features a single stage and is meant to be a complete experience as is — there are currently no plans to expand it. I do have some potential ideas for other stages, but there are many other projects I'd like to get back to for now.

Praxis Fighter X is also available on itch.io.

Update 2022/2/12

  • Improved outline rendering for aimed bullets (should improve visibility)
  • Added a pause menu option to disable shadows, as some players found them visually distracting/confusing
  • Added a new background tile featuring green sludge leaking over a riverbank

Update 2022/2/2

  • Fixed menu inputs being sometimes ignored on devices that can only run the game at 30fps
P#141025 2024-02-02 16:45 ( Edited 2024-02-12 15:56)

[ :: Read More :: ]

Cart #picopelago-0 | 2023-09-11 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
16

Create landscapes or sit back and watch them change over time

Picopelago is a terrain toy in 1024 bytes. It includes a screensaver mode which generates landscapes and a draw mode where you can shape them yourself

As time passes, erosion smooths out rugged terrain

As mountain peaks reach the sky, the landscape sinks into the sea

Controls

  • (▲)/(▼)/( X ) to navigate main menu
  • Mouse or touchscreen required for draw mode
  • Enter and choose "Reset Cart" to return to main menu

About

Created for PICO-1k Jam 2023 (code is 1024 bytes after minification & compression)

Commented source code (github)

P#134179 2023-09-11 16:14 ( Edited 2023-09-14 13:24)

[ :: Read More :: ]

Cart #playerpiano-4 | 2022-12-16 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
150

On this day 154 years ago, Scott Joplin was born. Happy birthday to the King of Ragtime!

Here's a rendition of his famous Maple Leaf Rag, with accompanying animation.

I've been trying to learn this piece on piano for a while. At some point I thought it might be fun to try transcribing it in PICO-8, and this is the result! Information on the music and animation is below:

Music

I tried to stay as true to the score as possible - since I was limited to only 4 channels, I had to make creative use of instruments and effects. So even though it's all just piano, there are 5 different instruments used:

  • normal held note
  • held note that's one octave down (for the low note at the start of the run that goes all the way up the piano)
  • held note with simultaneous octave down (using detune)
  • staccato note
  • staccato note with simultaneous octave down (using detune)

Since this piece makes heavy use of octaves in the bass and the accented parts of the melody, the detune trick was a huge help in getting all of the notes in (there are a lot of notes!). The detune emphasizes the lower note over the higher one, but I think it sounds convincing enough. Even with this trick, there were a handful of places I had to leave a note out of a chord, but I tried to leave out notes which were the least important/noticeable (eg. repeated nearby in either the bass or melody, or notes less important to the character of a chord, like prioritizing the 3rd/7th over the 5th). I think unless you know this piece really well and closely watch the animation, it would be pretty hard to notice the missing notes.

There weren't quite enough sfx slots to fit the last section of the piece, so I ended up arranging that in a separate cart and then putting it into the map data using CSTORE(). Partway through playback, I swap out some sfx that are no longer needed for the ones used in the last section.

Animation

The animation is all dynamic, based on the SFX data. Which is nice, because once I got it working I could change the sfx or add new parts and the animation would continue to reflect the music.

As for how it works, I wrote a function GETNOTES() which takes as parameters a time offset and returns a list of the notes that would be playing. I'm using STAT(54) to get the index of the currently playing pattern, and STAT(56) to get the current tick within it. With that information, I can add the time offset and use PEEK() to determine which pattern/sounds would be playing at the requested time and get the note information. By checking the pitch along with which instrument voice is playing a given note, I can figure out which keys should be pressed. For the detune notes that means playing two keys at once an octave apart. For staccato notes or notes with the fade out effect the key lets off earlier in the beat.

For the animation of the actual keys, I call GETNOTES() without a time offset to get the currently playing notes. Each of the keys is rendered with a sprite, and we can look up whether the key's note is being played to determine whether we should draw its pressed or unpressed sprite.

For the piano roll, I call GETNOTES() with a different time offset for each line, and for all the notes returned I use PSET() to change the colour at the coresponding spot on the roll (adjusting for perspective and scaling so the 88 notes of the piano span 44 horizontal pixels). Since it's all offset based on what's currently playing, doing this every frame naturally makes the dots scroll down the screen as if the roll is moving.

Other thoughts

If you'd like to see how it works in more detail, the code is reasonable well-commented so take a peek inside :) You could probably use a similar approach to automatically synchronize things in a rhythm game.

I also think it would be cool to transcribe more pieces of piano music and make this into a series -- you could even fit multiple songs in a single cart by using PX9 compression or similar, since the current code doesn't use very many tokens/characters. But it took me quite a while to transcribe just this one piece, and styles with more sustained notes / big chords wouldn't work well with PICO-8's limitations, so I'll leave it at this for now.

P#121275 2022-11-24 16:05 ( Edited 2022-12-16 17:19)

[ :: Read More :: ]

Cart #intoruins-6 | 2023-06-22 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
221


Reach depth 16 and retrieve the Wings of Yendor

Controls

(◀)(▶)(▼) : Turn

(▲) : Step forward / Attack / Interact

(X) : Open inventory

(O)/Z/C : Wait 1 turn

Into Ruins is a roguelike for the PICO-8 Fantasy Console.

There are no stairs. Jump down holes in the ground to make your way to the bottom. You will encounter natural cave formations, crumbling dungeon rooms, and terrifying creatures on your way.

There are no doors. Tread carefully or the creature in the next room might spot you. Light helps you explore more easily, but also reveals you to your foes. Fires can spread wildly through the environment, while glowing mushroom spores can mend your wounds.

There are no classes or character levels. Expand your abilities by finding magical items on your descent, and bring them to their full potential with Orbs of Power.

Each attempt is a new beginning — the different types of orbs, staves, cloaks and amulets will not be immediately recognizable to you. Some experimentation is required to identify them.

Credits

Into Ruins was created by Eric Billingsley. It was greatly inspired by Brogue by Brian Walker.

Thanks to FReDs72, Heracleum, James Edward Smith, morgan, Oli414, Sim, SlainteES, SmellyFishstiks and Waporwave for beta testing and feedback.

Technical Info

I figured some of you might be interested in some of the technical aspects of the game. This was my first foray into PICO-8 and Lua, and I learned a lot along the way -- from discovering with shock that variables are global by default after a week and a half of strange bugs, to trying to cram as much as possible into strings, and _ENV abuse to reduce token count.

How does it all fit?

Initially I thought I could fit everything into 1 cart, but partway through I realized that even if I could fit all the features I had planned in while respecting the token limit, the compressed character limit would be a problem.

The final version of the game is in 2 carts -- one for the title screen and text intro, and another for the main game. The title screen cart also puts a huge string containing most of the game's data (creature stat blocks, animation data, item stats, text descriptions) into the upper memory region, which is then read and parsed by the main cart. Because the title screen still displays the environment and character, there's a lot of similar code between the two carts, though a lot of things that were refactored to save tokens in the main cart are still in the title cart in their original form.

Even with these measures, the main cart still needed to be stripped of comments, newlines, and whitespace using shrinko8 to fit within the compressed character limit.

As far as tokens go, making almost everything in the game be the same type of object went a long way. Each object has a type (which is just its sprite number), and from that we can initialize its properties with a combination of default values and ones read from the huge string mentioned earlier. Having everything be intialized and accessed the same way means that each object has a lot of data it doesn't actually need, but we aren't strapped for Lua memory so that's okay! The upside is we can reuse code where appropriate, and make things interact more easily. To save tokens on accessing properties, I also made heavy use of the _ENV trick outlined by slainte here, and seleb's list of token optimizations was indispensible as well.

Level generation

The levels are generated using a couple different algorithms. Levels can contain natural caves, manmade dungeon rooms, or both. In either case, there is a variable called Entropy which ticks down as more of the level is generated.

Caves

Caves are made recursively using a type of random walk. From our current position, we try to generate another tile in a random neighbouring space. We then decide whether to recurse again from this position or backtrack -- this probability is based on the current entropy, so at the start we always continue, and by the end backtracking is more likely. Playing with the starting entropy and the amount it goes down by with each recursion allows us to achieve the right density and balance between narrow tunnels and wide open areas.

When choosing which tile to put in a new space, we consider the tile we are generating from. Each tile type has a corresponding section of the map data which defines 16 possibilities for the next tile, so we choose one at random from these. In a way this is like a Markov Chain, where the state is just based on the tile we are coming from. Holes, grass and other environmental features are all generated this way. The tile definition can also include an entity to generate, like a mushroom, brazier, chair or barrel.

With each recursion, there's a very small chance we switch to generating rooms.

Rooms

For rooms, we choose a random point on the map which will be contained in the room, and then random values for the room's width and height. The boundaries of the room are clamped to multiples of 2 or 4 to get them to lock together well and play nice with the rendering. We then create the floor and walls of the room, with one dimension being staggered to fit properly into the hex grid. Each room gets a random crumble value, which we use to decide if we should replace parts of the wall with different tiles (selected in the same way as in the cave generation).

After generating each room, there's a chance we switch to cave generation. Otherwise, we keep generating rooms at random positions until the entropy reaches 0. The rooms can overlap each other, and each time we generate one, a random openplan variable sets whether it should have walls on all sides, or try to merge with any overlapping rooms. In this way, we can get all kinds of interesting dungeon shapes just by combining rectangles.

Post-processing

Once the initial generation is complete, we analyze the level to find parts that are inaccessible, and then find a nearby position the player can get to and connect them in a straight line, either by building bridges or tunneling through walls. Then we recurse randomly through the level and fill out the contents of the manmade rooms, in the same way we did with the caves earlier.

There are a few more steps, like creating cave walls, ensuring there are exit holes, and running the code to connect areas again at various points. One interesting step is to replace cave walls which have walkable tiles both below and above them with a random tile, again sampled from the map data. This is actually the only way that stalagmites and mushrooms can be generated, and I think it makes their placement a bit more natural looking.

Spawning

With the environment generated, all that's left is to spawn creatures and items.

Creature spawn groups are again defined in the map, with each line corresponding to a different depth. We attempt 6 times to spawn a group of creatures, either choosing a group from the current depth or sometimes randomly from a greater one for a nasty surprise. We pick a random position, and try to spawn a creature there, and then move on to a to a neighbouring tile for the next one in the group. If it turns out the spawn point is invalid, we don't try again, just move on. This means some levels, especially smaller ones, will tend to have fewer creatures but that's okay -- it just adds variety!

Items are spawned in a similar way, with the depth just affecting how many we try to spawn (we attempt to spawn slightly more items on earlier depths than on later ones). For certain important orbs, we make extra spawn attempts if there haven't been enough of them generated yet over the course of the game based on the current depth.

Wrap-up

There's a lot of other things that I think could be interesting to write about, like the lighting, FOV algorithm, or various aspects of the AI, but for now I think this post is long enough. Let me know if there's anything you'd like to hear more about!

Into Ruins is also available on itch.io, and the unminified code is up on Github.

v1.06 changes

  • Web player touch controls (itch & newgrounds only): removed ability to input a diagonal on the d-pad by pressing in between two directions.
  • Web player touch controls (itch & newgrounds only): removed ability to input simultaneous X and O by pressing in between both buttons.
  • Fixed cursor being off by 1 pixel vertically when aiming
  • Adjusted aiming prompt text alignment

v1.05 Changes

  • Added custom font
  • Clearer text descriptions for Orbs of Gravity and Light
  • Fixed searching enemies sometimes moving after spotting you
  • Fixed orbs not activating when thrown at enemies flying over a hole
  • Fixed Pink Jellies having the wrong icon when hit with an Orb of Data

v1.04 Changes

  • Increased chance that a given spawned item will be a "fun" orb (fire, ice, teleport) -- other items will spawn slightly less often as a result
  • Improved several movement animations (player & some enemies) that had incorrect timing

v1.03 Changes

  • Increased chance of spawning a weapon or staff until at least one has been spawned
  • Increased chance of spawning an uncursed wearable item until at least one has been spawned
  • Flesh Horrors have a new unique ability...
  • Frozen enemies no longer display "?" when switching to searching state
  • Fixed player appearing not to die on game over screen when killed while being pushed down a pit with slofall active
  • Fixed a visual glitch that would happen if you blinked to victory
  • Fixed player not casting light until taking a step after descending while on fire
  • Fixed creatures not casting light until next turn when they catch fire from being in light
  • Fixed tiles not catching fire if a burning creature moves onto it and either dies or stops burning immediately after
  • Fixed creatures with idle animations still animating while frozen

v1.02 Changes:

  • Improved fire animation (was intended to flip horizontally every 3 frames, but I accidentally broke this before release)
  • Changed text description for Orb of Light for improved clarity

v1.01 Changes:

  • You can now pick up <important item> even if your inventory is full.
  • Fix a rare bug where 2 Mirrorshards could teleport into the same space, with strange effects.
  • Increase Mirrorshard HP from 6 to 8
P#119614 2022-10-27 14:52 ( Edited 2023-06-22 21:32)

[ :: Read More :: ]

[sfx]

Here's a flute I made for a track in my current project. I tried to capture the breathy harmonics that happen on the attack of a note. After the note is held, it goes into a looping tremelo.

I didn't switch on the reverb here, but I am using it with medium reverb on the pattern in my track.

For a softer sound, you can use the fade-in effect (4), or alternate between fading in and hard attack for more variety in sound. I've also had success with using a fast arpeggio between 2 notes for a trill effect.

Feel free to use it in your projects under CC4-BY-NC-SA license. Enjoy!

P#117968 2022-09-26 03:06 ( Edited 2022-11-24 21:13)

Follow Lexaloffle:          
Generated 2024-03-28 16:34:44 | 0.071s | Q:26