Fluffy's Prince of Persia

Threads about other remakes and ports.
FluffyQuack
Vizier
Vizier
Posts: 80
Joined: June 6th, 2004, 7:05 pm

Fluffy's Prince of Persia

Post by FluffyQuack »

For the heck of it, I started a project a little over a week ago to write my own engine that runs Prince of Persia.

This is what progress looks like right now:


I've got level rendering mostly done (it took a while to get the "decal textures" rendering correctly on the walls), and I've started working on player movement.

I don't know how far I'll get with the project. If I get to the point where I've got the gameplay 100% replicated, I might try to tackle stuff like adding multiplayer or try to add support for Prince of Persia 2 (which will probably have some big challenges as there's no source code I can reference to figure out how exactly how stuff is handled in that game).

I'm trying to make the gameplay as close as possible to the original POP, but when given the choice I prefer to design more straightforward structures rather than replicating what POP does. For instance, internally, my coordinate system is pretty straightforward: a position of 1 represents 1 pixel in the graphical output, instead of what POP1 does on DOS where it still uses the coordinate system designed for the Apple II original and then converts it to the DOS 320x200 resolution at render time. Those changes will probably result in very small changes to the gameplay.

I've decided to try to move some stuff into scripts to reduce the amount of hardcoded arrays in the code with gameplay information. For instance, I've got all animations defined as external scripts. Here's what the turn animation looks like:

Code: Select all

[POP1_Kid_Turn]
Action Turn
Flip
MoveX 6; ShowFrame 45; Wait
MoveX 1; ShowFrame 46; Wait
MoveX 2; ShowFrame 47; Wait
MoveX -1; ShowFrame 48; Wait
MoveX 1; ShowFrame 49; Wait
MoveX -2; ShowFrame 50; Wait
ShowFrame 51; Wait
ShowFrame 52; Wait
Anim POP1_Kid_Stand
It's still a work-in-progress. For instance, I'd rather not refer to animation frames with magic numbers, so I want to implement a system where you can refer to animation frames using names.
FluffyQuack
Vizier
Vizier
Posts: 80
Joined: June 6th, 2004, 7:05 pm

Re: Fluffy's Prince of Persia

Post by FluffyQuack »

I kind of put this project on the backburner the last few months, but I went back to it a few weeks ago.

First of all, for the heck of it, here are some early screenshots I made in September.

This was at the very start of the project where I've got level data loaded and I'm rendering the general layout of level 2 in POP1, but without any actual graphical assets.
Screenshot0009.png
Screenshot0009.png (3.61 KiB) Viewed 9200 times

First room in POP1 with many graphical assets implemented:
Screenshot0015.png

First room again, this time with more graphical assets implemented, including correct rendering of walls:
Screenshot0022.png

Zoomed-out view of level 1:
Screenshot0024.png

And now for stuff I've done recently, during the last 1-2 weeks. I added a toggle which uses render-to-texture so I can apply aspect correction to final render:
Screenshot0039.png

I added loading of POP2 level data. I haven't implemented graphics from POP2 yet, but here's the general layout of level 3 in POP2 rendered using POP1's dungeon tileset:
Screenshot0041.png

I started implementing the POP1 palace tileset. The wall rendering was a little bit of a doozy to implement (I kept looking for the main wall texture, but then I looked into SDL-PoP code and realized it's simply rendering solid colours). Here's an early version with wrong palette applied:
Screenshot0042.png

And here it is now after loading the correct palette and figuring out how SDL-PoP handles colours for the walls (I also added code for rendering the bottom 3 pixels of walls):
Screenshot0045.png
FluffyQuack
Vizier
Vizier
Posts: 80
Joined: June 6th, 2004, 7:05 pm

Re: Fluffy's Prince of Persia

Post by FluffyQuack »

I did some work on optimizing my render code. Not really necessary to do as I can easily hit 12fps while rendering the same amount of tiles as POP1, but I don't like having overly inefficient code. The primary bottlenecks I had were constant binding to different textures and some unnecessary overhead when drawing tiles.

Instead of loading each graphical asset as its own texture, I started working on a system that compiles all assets into one "mega" texture atlas. This is the texture atlas the code generates right now:
POP-MegaTextureAtlas.png

Some things to note:
  • I don't need to, but I'm loading both POP1 and POP2 assets at the same time. The stuff you see in the atlas is the stuff I've added to my loading lists so far. There's still a lot missing, but I think I can actually easily fit all the graphics from POP1 and POP2 into one texture.
  • Some of POP2 assets look like gibberish because I'm loading them with the wrong palette.
  • If you zoom in, you can see pixel doubling along the edges of sub-textures. This is padding so that texture sampling won't ever grab from neighbouring sub-textures. I had that happen when I rendered graphics at a lower scale than 100%.

One of the first attempts of rendering with the texture atlas. I kind of forgot about OpenGL's texture coordinate system where 0,0 is bottomleft of a texture rather than topleft, whoopsie:
2022-12-11 14-55-28-535.png

After getting everything implemented correctly, here's a stress test at 1920x1080 rendering everything at 50% scale. With my old code, this would give me 43 FPS. With the texture atlas and other improvements to the rendering code, I now get around 700 FPS in the same scene:
2022-12-11 18-10-31-220.png

By the way, this is what rendering looks like if I don't add padding between sub-textures in the texture atlas. You can see random artifacting all over the place:
2022-12-11 15-11-50-447.png
FluffyQuack
Vizier
Vizier
Posts: 80
Joined: June 6th, 2004, 7:05 pm

Re: Fluffy's Prince of Persia

Post by FluffyQuack »

As one method for understanding all of the collision systems in POP, I'm modifying SDL-PoP to output debug graphics visualizing various collision data. This is the mess I'm generating right now:
prince-fluffymod 2022-12-27 23-05-31-909.png
  • White rectangle around the player is the collision box used for collision checks against walls. It only has 2 horizontal coordinates (char_x_left_coll and char_x_right_coll). It looks like the horizontal coordinates are generated from size of the image data, though certain animations frames are flagged as "thin" which means they become vastly thinner for a short amount of time (this is defined for the standing turning animation and I suspect it's done to prevent wall collision mid-turn).
  • Purple line below the player indicates the current tile the player belongs to. I think I got the horizontal length and position of it correct, so if the player steps outside of that line, the player will then belong to a different tile.
  • The small purple vertical line attached to the horizontal line represents where the prince currently has his feet placed (attained from dx_weight()). This is used to determine what tile the prince belongs to.
  • The yellow vertical lines correspond to the horizontal placement of wall collision, which is the middle point of a tile.
  • The debug text top-right show a few player coordinates. KidY is the vertical position and KidX is the horizontal position (using the dx_weight() value). Note that the X positon uses a very different coordinate system than the screen space coordinate system.
FluffyQuack
Vizier
Vizier
Posts: 80
Joined: June 6th, 2004, 7:05 pm

Re: Fluffy's Prince of Persia

Post by FluffyQuack »

Progress towards rendering a POP2 level:
2023-01-10 21-51-57-157.png
FluffyQuack
Vizier
Vizier
Posts: 80
Joined: June 6th, 2004, 7:05 pm

Re: Fluffy's Prince of Persia

Post by FluffyQuack »

Early progress towards rendering lvl6 in POP2 (the first Ruins level):
2023-01-18 14-58-54-075.png
I'm focusing on figuring out what looks like the most fiddly part with rendering each tileset, which so far is learning what the spec variables mean. The second spec byte in particular is used to define a lot of variants for graphical assets and you're supposed to do bunch of bit shifts and use bitmasks to get a real value from it.

For Caverns tileset:
  • First 3 bits are for defining what type of "bottom" variant to render for foreground walls. 7 different variants.
  • Bit shift by 3 to the right, and you get the value for the type of stalactite hanging from a ceiling or tile. Each one has a unique render offset and there are 7 different ones.
For Ruins tileset:
  • First 4 bits are for defining what type of "decal" to draw on a foreground wall or floor tile. I'm not sure how many unique ones there are. All of these have unique render offsets, and some of them need to rendered in a specific order. Most are wall decals (render last), but some are supposed to be rendered in the middle of a tile.
  • Bit shift by 4 to the right, and you get the value for the type of graffiti to draw on a background wall. Each one has a unique render offset and there are 3 different ones.
FluffyQuack
Vizier
Vizier
Posts: 80
Joined: June 6th, 2004, 7:05 pm

Re: Fluffy's Prince of Persia

Post by FluffyQuack »

Early progress for rendering POP2 Temple tileset:
2023-01-23 20-42-06-902.png
Took me a bit of time to figure out how exactly I'm supposed to be rendering the foreground wall tiles.

It's three textures needed:
  • Render shape #3524 as primary tile art
  • If left tile is also a wall, then also render 3561
  • If below tile is also a wall, then also render 3563 (3562 is a similar variant, but I'm not sure where that is used)
FluffyQuack
Vizier
Vizier
Posts: 80
Joined: June 6th, 2004, 7:05 pm

Re: Fluffy's Prince of Persia

Post by FluffyQuack »

I went back to this project about a month ago and got a lot of progress related to gameplay -- specifically player movement. I decided to switch up the coordinate system so it's similar to how it is in the original to make it easier to replicate the gameplay. Though, if I want to replicate POP2 properly, then I'll need to go with what I originally intended (where 1 unit of movement corresponds to one pixel on screen) as that's how POP2 works.

I've implemented every movement the POP1 Prince can do except for combat and picking up items, and they should behave exactly the same as in POP1. I also implemented moving between levels (all levels exist in memory all the time and the game can simulate all of them simultaneously, which will be useful in the long run when I implement multiplayer)

Here's what the current gameplay looks like:


I think my next step will be to implement behaviour for the various tiles: pressure plates, loose tiles, gates, etc.

I'm kind of dreading trying to replicate POP2's gameplay. It's such an enormous benefit being able to look up SDL-POP's source code or the original Apple 2 source code. We have a partial commented disassembly of POP2, which is useful, but being able to look up the POP1 source code almost feels like using a cheat sheet as it makes it so fast to figure out how something works in exact detail.
FluffyQuack
Vizier
Vizier
Posts: 80
Joined: June 6th, 2004, 7:05 pm

Re: Fluffy's Prince of Persia

Post by FluffyQuack »

I haven't had as much time as I had hoped working on this, but I got some progress lately and I figured I'd record a video while I still have a funky build with these constantly animating gates and doors:
FluffyQuack
Vizier
Vizier
Posts: 80
Joined: June 6th, 2004, 7:05 pm

Re: Fluffy's Prince of Persia

Post by FluffyQuack »

It's now almost exactly one year since I started the project. This is the current state:


I've made pressure plates, gates, and exit door function as they should. Spikes and chompers are nearly finished. Loose tiles function as they should, though they have an incorrect position for one frame as they start falling.

I think the biggest missing feature is enemies, but after that 99% of the game content should be working as it should, though I'll still need to fix up render order, add rendering of full Palace tileset, add sounds and music, and add support for scripted sequences.

While working with the characters I'm ensuring it'll work correctly with multiple players. Local multiplayer will be extremely easy to implement. I want online multiplayer as well, and that will require more work, but shouldn't be hard.
FluffyQuack
Vizier
Vizier
Posts: 80
Joined: June 6th, 2004, 7:05 pm

Re: Fluffy's Prince of Persia

Post by FluffyQuack »

I haven't had too much time to work on this the past month, but I got some work done. Here's the current state:


I added loading and playback of sound, did some work on render order (which you can clearly see still needs more attention), and added a function for spawning additional players.
User avatar
Alberto
Sheikh
Sheikh
Posts: 48
Joined: February 13th, 2019, 6:55 am
Contact:

Re: Fluffy's Prince of Persia

Post by Alberto »

I really like the zoom out effect. Ever since I was little I wanted to see the entire map at the same time. This is a nice addition I personally welcome. I wish there were more ports with such kind of improvements.
FluffyQuack
Vizier
Vizier
Posts: 80
Joined: June 6th, 2004, 7:05 pm

Re: Fluffy's Prince of Persia

Post by FluffyQuack »

Alberto wrote: December 27th, 2023, 9:54 am I really like the zoom out effect. Ever since I was little I wanted to see the entire map at the same time. This is a nice addition I personally welcome. I wish there were more ports with such kind of improvements.
Yeah, it's a fun feature and it was very easy to implement in my codebase with how I've set up level loading (I'm loading each level into big seamless grids rather than being divided into rooms). I assume this is a rare feature in POP ports because the original game is very much hardcoded to just show one room at a given time, so if you've structured the code the same way, then you don't have a straightforward way to implement multi-room rendering.
User avatar
Alberto
Sheikh
Sheikh
Posts: 48
Joined: February 13th, 2019, 6:55 am
Contact:

Re: Fluffy's Prince of Persia

Post by Alberto »

FluffyQuack wrote: December 29th, 2023, 6:21 pm Yeah, it's a fun feature and it was very easy to implement in my codebase with how I've set up level loading...
Oh wow, so the main code is 'unchanged' (as in faithful to the original) and you upgraded the level loading? Nice!

It would be funny watching the guards stand one foot away making eye contact with the Kid, but not following him because he is in another 'room' :lol: Or it would be interesting watching how the Kid phases through the gate in Level 5 :P (Trick 35)
Last edited by Alberto on January 1st, 2024, 6:56 am, edited 1 time in total.
FluffyQuack
Vizier
Vizier
Posts: 80
Joined: June 6th, 2004, 7:05 pm

Re: Fluffy's Prince of Persia

Post by FluffyQuack »

Alberto wrote: December 31st, 2023, 10:21 am Oh wow, so the main code is 'unchanged' (as in faithful to the original) and you upgraded the level loading? Nice!

It would be funny watching the guards stand one foot away making eye contact with the Kid, but not following him because he is in another 'room' :lol: Or it would be interesting watching how the Kid phases through the gate in Level 5 (trick 35). :P
My gameplay code is very faithful overall, but it's structured differently than the original game. One big difference is that a level isn't divided into rooms in my project. Original POP has to look up information from other rooms when the player is off-screen, but in my project I always look up absolute tile coordinates no matter where a player is positioned. So I'm 99% sure that gate bug you linked won't happen in my code. I assume it happened in the original code because it failed to acquire information about a specific tile from a different room, but my code always does tile lookups the same way.

That said, my code does calculate where the room boundaries are supposed to be and it's used for various checks. So, you'll see an enemy completely lose interest once you cross the magical room boundary (assuming he's not too close, in which case he'll keep chasing the player just like the original game). Of course, it's also used for the camera to ensure it's focusing on the room the player is in.

I'm not completely sure how I'll handle things in the long run. Right now, I'm trying to make sure gameplay is as faithful as possible (though I'm completely okay with bugs being fixed), but there are some changes I wouldn't mind seeing personally. I think it could be fun to have guards chase you indefinitely once they spot you. Right now, the collision code works the same way as the original game but I wouldn't mind rewriting it from scratch so it's easier to work with, and that would also fix some rare bugs related to collision.

This shouldn't change gameplay, but I got one idea when looking more closely at the combat animations. Enemies and the player use the exact same frame data for every combat frame, but there's one visual difference: the prince has 2 frames of animations for parrying ("start parry" and the "actual parry" frame), but the enemies only use one (they show the "en garde" frame instead of "start parry" if I recall). I think it would be interesting to see that expanded so they also show 2 frames, but that would require new graphical assets.

On a related note, I'm planning to make it possible to make the enemies playable. The enemies are controlled a similar way as the player (the AI simulates key presses), so it wouldn't be much work to make them playable (the downside is that they don't have many movement options available, but it could be a fun competitive mode maybe to have one player control the prince and another player control a guard).

Since I haven't written an update on the project in a while, I can quickly mention my current schedule:
  • Finish up all gameplay functionality in POP1 (right now, that means finishing up enemy AI and adding a proper death state for players)
  • Start implementing support for POP2 gameplay (I've been looking at David's POP2 disassembly and that's been an extremely helpful resource. My plan is to expand on that and map out more POP2 gameplay code that I'll need to reference)
  • Implement "scripted events" in POP1 and POP2 (ie, the shadow prince stuff in POP1)
  • Rewrite some code so it's easier to work with (for instance, re-write collision check code)
I'm not sure what pace I'll be working at, though. The amount of time I can allocate to this project varies greatly, so it's hard to judge when I'll hit the various milestones.
Post Reply