Skip to content

Instantly share code, notes, and snippets.

@f0ursqu4r3
Last active November 22, 2023 06:09
Show Gist options
  • Save f0ursqu4r3/d96c79bc6f1c75c1d846433efb6ceda9 to your computer and use it in GitHub Desktop.
Save f0ursqu4r3/d96c79bc6f1c75c1d846433efb6ceda9 to your computer and use it in GitHub Desktop.
{
"stacks": [
{
"name": "house",
"dimentions": {
"width": 32,
"height": 32
},
"slices": 16,
"path": "/Users/kyle/Documents/resources/house_ss.png"
},
{
"name": "rock",
"dimentions": {
"width": 16,
"height": 16
},
"slices": 4,
"path": "/Users/kyle/Documents/resources/rock_ss.png"
},
{
"name": "tree",
"dimentions": {
"width": 48,
"height": 48
},
"slices": 48,
"path": "/Users/kyle/Documents/resources/tree_ss.png"
},
{
"name": "cube",
"dimentions": {
"width": 32,
"height": 32
},
"slices": 32,
"path": "/Users/kyle/Documents/resources/cube_ss.png"
}
]
}
from __future__ import annotations
import itertools
import random
import json
import math
from dataclasses import dataclass
import pygame as pg
from pygame import Vector2 as vec2
class Game:
def __init__(self, width:int, height:int):
pg.init()
self.window = pg.display.set_mode((width, height))
self.win_size = vec2(width, height)
self.disp_scale = 2.0
self.clock = pg.time.Clock()
self.running = False
self.dt = 0.0
self.y_offset = 1
self.camera = Camera(
frame=self.win_size/self.disp_scale,
position=-self.center,
angle=0.0
)
self.spritestacks = self.load_spritestacks()
self.objs = [
Object(
kind='cube',
position=vec2(),
angle=0.0
)
]
self.selected = None
@property
def screen_size(self):
return self.win_size/self.disp_scale
@property
def center(self):
return self.screen_size/2
def run(self):
self.running = True
while self.running:
self.handle_events()
self.update()
self.draw()
def handle_events(self):
for event in pg.event.get():
if (event.type == pg.QUIT or
(event.type == pg.KEYDOWN and
event.key == pg.K_ESCAPE)):
self.running = False
elif event.type == pg.KEYDOWN:
if event.key == pg.K_UP:
self.obj.angle += 5
elif event.key == pg.K_DOWN:
self.obj.angle -= 5
elif event.type == pg.MOUSEMOTION:
if event.buttons[0]:
if pg.key.get_pressed()[pg.K_SPACE]:
screen_movement = vec2(event.rel)/self.disp_scale
world_movement = screen_movement.rotate(self.camera.angle)
self.camera.position -= world_movement
if pg.key.get_pressed()[pg.K_LSHIFT]:
rel = vec2(event.rel)/self.disp_scale
self.y_offset -= rel.y*0.05
self.y_offset = min(1, max(self.y_offset, 0))
self.camera.angle += rel.x
elif event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1:
mpos = vec2(event.pos)/self.disp_scale
self.selected = None
objs = sorted(
[
[obj, apply_camera_transform(obj.position, self.camera, self.y_offset)]
for obj in self.objs
],
key=lambda o: o[1].y,
)
for obj, pos in reversed(objs):
if (pos - mpos).length() < 16:
self.selected = obj
break
def update(self):
self.dt = self.clock.tick(60) * 0.001
fps = self.clock.get_fps()
pg.display.set_caption(f'{fps:0.2f}')
# for i, obj in enumerate(self.objs):
# obj.angle += ((i+1)*10)*self.dt
def draw(self):
self.window.fill((200,200,200))
screen_surf = pg.Surface(self.screen_size, pg.SRCALPHA)
objs = sorted(
[
[obj, apply_camera_transform(obj.position, self.camera, self.y_offset)]
for obj in self.objs
],
key=lambda o: o[1].y,
)
for obj, pos in objs:
if (
pos.x < -48 or
pos.x > self.screen_size.x + 48 or
pos.y < -48 or
pos.y > self.screen_size.y + 48
):
continue
if self.selected == obj:
pg.draw.circle(screen_surf, (0,200,0), pos, 16, 1)
slices = self.spritestacks[obj.kind]['slices']
scale = map_value(pos.y, 0, self.screen_size.y, .75, 1.25)
for y, surf in enumerate(slices):
apos = pos + vec2(0, (-y * self.y_offset))
layer = pg.transform.rotozoom(
surf,
obj.angle + self.camera.angle,
scale
)
rect = layer.get_rect()
rect.center = apos
screen_surf.blit(layer, rect.topleft)
self.window.blit(pg.transform.scale(screen_surf,self.win_size), vec2())
pg.display.update()
def load_spritestacks(self):
with open('/Users/kyle/Documents/resources/spritestacks.json') as f:
stacks = {
item['name']: item
for item in
json.load(f)['stacks']
}
for name, stack in stacks.items():
width = stack['dimentions']['width']
height = stack['dimentions']['height']
sheet = pg.image.load(stack['path']).convert_alpha()
stack['slices'] = [
sheet.subsurface((x*width, 0, width, height))
for x in range(stack['slices'])
]
return stacks
def world_to_screen(pos: vec2, camera: Camera) -> vec2:
translated_pos = pos - camera.position
screen_pos = translated_pos.rotate(-camera.angle)
return screen_pos
def screen_to_world(pos: vec2, camera: Camera) -> vec2:
world_pos = pos.rotate(camera.angle)
world_pos += camera.position
return world_pos
def apply_camera_transform(object_pos, camera, y_offset=0):
rotated_pos = rotate_around_pivot(object_pos, camera.position+camera.frame*0.5, -camera.angle)
translated_pos = rotated_pos - camera.position
mapped_y = map_value(translated_pos.y, 0, camera.frame.y, -1, 1)
return translated_pos - vec2(0, mapped_y * 32 * y_offset)
def rotate_around_pivot(object_pos:vec2, pivot_pos:vec2, angle:float):
offset = object_pos - pivot_pos
rotated_offset = offset.rotate(angle)
new_pos = pivot_pos + rotated_offset
return new_pos
def map_value(old_value, old_min, old_max, new_min, new_max):
return (((old_value - old_min) * (new_max - new_min)) / (old_max - old_min)) + new_min
@dataclass
class Camera:
frame: vec2
position: vec2
angle: float
zoom: float = 1.0
@dataclass
class Object:
kind: str
position: vec2
angle: float
Game(512, 512).run()
@f0ursqu4r3
Copy link
Author

f0ursqu4r3 commented Nov 18, 2023

truck_ss
house_ss
rock_ss
tree_ss

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment