Greeting, stranger! I'm happy to share my first game project with you. Right now, this game is in development. So, please, feel free to leave your feedback in the comments section below! It will help me very much :)
In Comrade Architect, the you will have to try on the skin of an architect of space settlements in the distant future. Your duties include placing the number of colonists set by the party on the surface of the planet. To successfully cope with this mission, you need to extract resources for construction and provide the colonists with food and shelter. You have to keep in mind that you need to build towers to expand the zone of influence. Otherwise, the place for the building will quickly end.
Specific constraints for the colonization of each planet add challenges. The better the architect copes with his duties, the more worlds are available to him for colonization.
Planned features:
One of the most common problems of pixel art games is supporting multiple resolutions. The difficulty is because such graphics can’t be scaled with any ratio you need without artifacts. In this article, I’ll show different approaches to solving this issue.
The most common way is to use Stretch in project properties. Godot documentation describes this very clearly here. But stretching doesn’t solve the problem of scaling pixel art graphics. Here is the approach I’ve used in my game — Comrade Architect.
First of all, we need to construct a scene with game objects. I didn’t mind about resolutions at this point. In Comrade Architect, I need to draw available tiles in the middle of the screen. But what if the available space is much bigger than my game view? I needed to scale up objects view somehow. I knew that it’s possible to scale pixel art by an integer factor. So, I’ve used a camera with a script in it, which knows how to show a rectangle. Here it is:
class_name PlanetCamera
extends Camera2D
export var allow_downscale: bool = false
export var precise_zoom: bool = false
var current_rect: Rect2
func _process(_delta):
if current_rect != null:
_perform_show_current_rect()
func show_rect(rect: Rect2):
current_rect = rect
position = rect.position + Vector2(rect.size.x / 2, rect.size.y / 2)
_perform_show_current_rect()
func _perform_show_current_rect():
var viewport_rect: Rect2 = get_viewport_rect()
var viewport_size = min(viewport_rect.size.x, viewport_rect.size.y)
var tile_map_size = max(current_rect.size.x, current_rect.size.y)
if tile_map_size == 0:
return
var new_zoom
if viewport_size == 0:
new_zoom = 1
else:
new_zoom = tile_map_size / viewport_size
if precise_zoom:
zoom = Vector2(new_zoom, new_zoom)
return
if new_zoom > 1:
if allow_downscale:
var _new_zoom = 1.0
while _new_zoom < new_zoom:
_new_zoom *= 2
new_zoom = _new_zoom
else:
new_zoom = 1
else: if new_zoom == 1:
new_zoom = 1
else:
var _new_zoom = 1.0
while 1 / _new_zoom > new_zoom:
_new_zoom += 1
new_zoom = 1 / (_new_zoom - 1)
zoom = Vector2(new_zoom, new_zoom)
As you can see, I increment the scale by an integer amount until I get as close to the desired value as possible. Now I had all game objects centered in a viewport.
The next step is to build a UI for the game. I’m an Android developer by profession, so the solution came to me from my day-to-day routine. On mobile devices, we use adaptive layouts and resizable vector backgrounds to support many different screens. Godot has several ways to create scalable pixel art UI. The first is to use NinePatchRect as a background. I don’t advise this approach because it doesn’t work with themes and different UI states. Better stick with the second one. It’s about configuring margins in StyleBoxTexture. You only need to select an area to stretch on scaling. Then you can set the result to any Control you need, from Button to Panel.
Now it’s time to combine it all. I’ve used a ViewportContainer to add the game objects to my UI. I liked this approach because it gives full control over player view. At this point, game objects were scaling nice, but UI elements were too little on HIDPI screens. I added a global script to manipulate root Viewport stretch to adjust UI scale to screen size. The idea was to use the same approach as for game objects. I increment the Stretch setting until it’s below the desired amount. Here is the script, which I added to autoload:
extends Node
var project_size = Vector2(
ProjectSettings.get_setting("display/window/size/width"),
ProjectSettings.get_setting("display/window/size/height")
)
var current_scale = -1
func _ready() -> void:
OS.min_window_size = Vector2(project_size.x, project_size.y)
func _process(_delta: float) -> void:
var new_scale = _calculate_interface_scale()
if new_scale != current_scale:
get_tree().set_screen_stretch(
SceneTree.STRETCH_MODE_DISABLED, \
SceneTree.STRETCH_ASPECT_EXPAND, \
Vector2.ZERO, \
new_scale
)
current_scale = new_scale
func _calculate_interface_scale() -> int:
var window_size = OS.get_window_size()
var desired = min(project_size.x, project_size.y)
var current = min(window_size.x, window_size.y)
var scale = 1
while current / scale > desired:
scale += 1
return max(scale - 1, 1) as int
Here I used the desired project screen size to determine the scale factor. Pixel art can’t be downscaled without artifacts, so I didn’t allow to resize the window smaller than the minimum. Now I had a brilliant solution that supports different scales for game objects and UI without artifacts.
You can check the result on Itch.io. Thanks for reading!
Only registered members can share their thoughts. So come on! Join the community today (totally free - or sign in with your social account on the right) and join in the conversation.