- Edited
Moving a project over from Unity into Godot. It uses a 3D pixel art art-style similar to the works of t3ssel8r. Currently, I've been able to get a pixelated look using the resolution settings in Project Settings > Display > Window. However, I've been having a lot of trouble getting rid of pixel creep/swimming which happens when the camera moves.
I tried multiple implementations (including trying to port multiple from Unity) but nothing was able to work. Closest I've been able to get so far was with the help of this reddit comment by denovodavid which gives some code for achieving subpixel snapping. In a reply to this comment, you can see a gif of someone who successfully implemented denovodavid's code and the result is exactly what I want.
However, I have not been so lucky. The best result I've got is this but the camera has this jittery movement which I have not been able to solve.
My code is as follow (lightly modified version of denovodavid's code. Variable names have changed, added a texturerect which is offset by the subpixel error as per denovodavid's instructions, and I have a lower resolution)
`
#Resolution is 320x180 with some padding to account for the screen texel error
const width: int = 322
const height: int = 182
const orthographic_size: float = 10
const texel_snap: float = float(height) / orthographic_size
@export var cam_proxy: Node3D
@export var cam_parent: Node3D
@export var render_texture: Control
@onready var cam :Camera3D = self.get_parent()
var last_rot
var world_space
func _ready():
INPUT_PROVIDER.camera = self.get_parent()
INPUT_PROVIDER.set_cam()
last_rot = cam_proxy.global_rotation
world_space = cam_proxy.global_transform
func _process(delta):
if cam_proxy.global_rotation != last_rot:
last_rot = cam_proxy.global_rotation
world_space = cam_proxy.global_transform
var snapped_pos = cam_proxy.global_position * world_space
var snapped_world_space_pos: Vector3 = floor(snapped_pos * texel_snap) / texel_snap
cam.global_position = world_space * snapped_world_space_pos
cam.global_rotation = cam_proxy.global_rotation
#use error to shift the final image on TextureRect/Sprite3D/etc. for extra smooth
var snap_space_error = snapped_world_space_pos - snapped_pos
var screen_texel_error = Vector2(snap_space_error.x, -snap_space_error.y) * texel_snap
#Smoothly move the render texture to the correct position
render_texture.set_position(screen_texel_error)
`
In case it matters, here's my hierachy layout
and as a final thing, here's my basic camera movement code I made for this test scene:
`extends Node3D
const BUTTON_MASK_LEFT = 1
const BUTTON_MASK_RIGHT = 2
func _process(delta):
var input_stateictionary = INPUT_PROVIDER.get_state()
var direction = input_state["input_vector"] if input_state.has("input_vector") else Vector3.ZERO
#Direction is just a vector3 based on what keys are pressed (i.e. W = (0,0,1), A = (-1,0,0), etc.)
if direction != Vector3.ZERO:
# Move the camera
var camera_transform = self.get_transform()
camera_transform.origin -= direction * 3 * delta
set_transform(camera_transform)`
This is attached to the CamProxy node which the camera is snapped to.
I'd really appreciate if anyone could give some input on how to achieve a pixel creep free and smooth camera movement for 3D pixel art games. This is a really big block for me right now and as far as I can tell, there is no open-source example for a smooth 3D pixel art camera for Godot and I would be nice to be able to make a test scene example of one other people can use.