A demo of an RPG with “2.5D” graphics: 2D characters in a 3D environment, inspired by Pokemon Black & White and Earthbound 2. This is a is a proof of concept for the style: the character sprite images and 3D building models are borrowed from these games, but the programming, camera work, and rendering pipeline are all original.

This project was revived after I lost the source code long ago and recovered it by decompiling an extant Windows build with DevXUnityUnpacker and trying to match behavior to a screen capture. Enjoy! 😃

Controls:

  • WASD keys to move
  • Left and right arrow keys to rotate the camera

The most challenging part of this project was getting the 3D and 2D components to play nicely, such that the 2D sprites still rendered pixel perfectly, despite being positioned/oriented in 3D, and adapating to different screen sizes.

To solve the first issue, the sprites are rendered as a quad in 3D, billboarding the camera in all axes, anchored at the sprite’s bottom, to make sure there’s no pixel imperfection from rotation.

Secondly, the camera is orthographic, to make sure there’s no pixel imperfection from scaling due to perspective. Technically, a perspective camera with a small FOV and very far away from the scene will accomplish something close enough, with some imperfection for sprites further away from the center, and still give some sensation of perspective.

Lastly, a special shader is used such that these quads are aligned to a pixel on the camera, so there’s no inter-pixel imperfection.

As a result, for the (fixed) vertical camera angle (downward looking) present in the demo, the quads are actually tilted up. This makes physics interactions interesting in that the quads can’t be used directly. Instead, a capsule collider with its radius set such that it protects the quad from clipping into vertical 3D walls. The same proxy can be used to provide shadows and other 3D effects.

As for screen size adaptation, the trick used here is to decide on a fixed design size (say SNES’s 256x224), and configure the camera’s orthographic size to the height/2, then render this to a 256x224 RenderTexture. The RenderTexture is then rendered onto a RawImage which is aspect-fit scaled to match the real window resolution, rendered by the final camera. This preserves the pixel-perfect quality of the artwork. This is also where any compositing, like palette switches might live.