[game] Factor into actor

This commit is contained in:
John Doty 2023-08-19 21:16:27 -07:00
parent c7ea93f972
commit efd6884d0b
2 changed files with 121 additions and 83 deletions

99
game/actor.ts Normal file
View file

@ -0,0 +1,99 @@
import { load_texture } from "./assets";
import { btn, Button } from "./input";
import { Vec2, new_v2, vadd, vnorm, vmul } from "./vector";
import { spr, use_texture, Texture } from "./graphics";
export class Actor {
velocity: Vec2 = new_v2(0);
friction: number = 0.6;
id: string;
position: Vec2;
bounds: Vec2;
constructor(id: string, position: Vec2, bounds: Vec2) {
this.id = id;
this.position = position;
this.bounds = bounds;
}
update() {}
update_physics() {
const velocity = vmul(this.velocity, this.friction);
// Zero if we're smaller than some epsilon.
if (Math.abs(velocity.x) < 0.01) {
velocity.x = 0;
}
if (Math.abs(velocity.y) < 0.01) {
velocity.y = 0;
}
const new_position = vadd(this.position, velocity);
// TODO: Collision detection
// const { w, h } = this.bounds;
this.velocity = velocity;
this.position = new_position;
}
draw(_clock: number) {}
bonk(_other: Actor) {}
}
const robo_info = {
width: 32,
height: 32,
sprite: "./bot.png",
animations: [
{ start: 0, length: 1, speed: 20 },
{ start: 1, length: 4, speed: 8 },
],
};
export class Robo extends Actor {
bot_sprite: Texture | undefined = undefined;
constructor(pos: Vec2) {
super("robo", pos, new_v2(robo_info.width, robo_info.height));
load_texture(robo_info.sprite).then((texture) => {
this.bot_sprite = texture;
});
}
update() {
// Acceleration from input
let a = new_v2(0);
if (btn(Button.Up)) {
a.y -= 1;
}
if (btn(Button.Down)) {
a.y += 1;
}
if (btn(Button.Left)) {
a.x -= 1;
}
if (btn(Button.Right)) {
a.x += 1;
}
vnorm(a);
this.velocity = vadd(this.velocity, vmul(a, 1.5));
}
draw(clock: number) {
if (this.bot_sprite != undefined) {
use_texture(this.bot_sprite);
const vel = this.velocity;
const moving = vel.x != 0 || vel.y != 0;
const anim = robo_info.animations[moving ? 1 : 0];
const { x: w, y: h } = this.bounds;
const { x, y } = this.position;
const frame = (anim.start + ((clock / anim.speed) % anim.length)) >> 0;
spr(x, y, w, h, frame * w, 0, 32, 32);
}
}
}

View file

@ -1,16 +1,12 @@
import { cls, print, spr, use_texture, Texture } from "./graphics";
import { load_texture } from "./assets";
import { cls, print } from "./graphics";
import { since_start } from "./time";
import { btn, Button } from "./input";
import { new_v2, vadd, vmul, vnorm } from "./vector";
import { new_v2 } from "./vector";
import { load_world, World, Level, draw_level } from "./level";
/// TODO: Support reload by saving and restoring state from init/update/restore.
import { Actor, Robo } from "./actor";
/// A nice looping frame counter.
let clock = 0;
let bot_sprite: Texture | undefined = undefined;
let world: World | undefined = undefined;
let level: Level | undefined = undefined;
@ -18,15 +14,8 @@ let level: Level | undefined = undefined;
// zelda screen is 16x11 tiles
// from a feeling point of view this is sufficient, apparently :D
let loaded = false;
function load_assets() {
// Start this load, but then...
let texture_load = load_texture("./bot.png").then((n) => {
print("Bot loaded at", since_start());
bot_sprite = n;
});
let map_load = load_world("./overworld.ldtk").then((w) => {
print("World loaded at", since_start());
world = w;
@ -36,106 +25,56 @@ function load_assets() {
if (!level) {
throw new Error("UNABLE TO FIND LEVEL AT 0,0: CANNOT START");
}
// TODO: SPAWN ACTORS BASED ON LEVEL.
});
Promise.all([texture_load, map_load]).then(() => {
loaded = true;
Promise.all([map_load]).then(() => {
print("All are loaded.");
});
}
const actors: Actor[] = [];
// TODO: Build a system whereby the signatures of the fundamental functions can be checked.
export function init() {
print("Hello world!");
load_assets();
actors.push(new Robo(new_v2(10, 10)));
}
export function suspend() {
return { clock };
}
export function resume(snapshot) {
if (snapshot) {
print("!Resuming!");
const { clock: new_clock } = snapshot;
if (new_clock) {
clock = new_clock;
}
}
}
const friction = 0.6;
let robo_vel = new_v2(0);
let robo_pos = new_v2(10);
export function resume() {}
export function update() {
clock = (clock + 1) % 20160;
// Acceleration from input
let a = new_v2(0);
if (btn(Button.Up)) {
a.y -= 1;
}
if (btn(Button.Down)) {
a.y += 1;
}
if (btn(Button.Left)) {
a.x -= 1;
}
if (btn(Button.Right)) {
a.x += 1;
}
vnorm(a);
a = vmul(a, 0.8); // Speed.
// Friction
let v = vmul(robo_vel, friction);
v = vadd(v, a);
// Zero if we're smaller than some epsilon.
if (Math.abs(v.x) < 0.01) {
v.x = 0;
}
if (Math.abs(v.y) < 0.01) {
v.y = 0;
for (const actor of actors) {
actor.update();
}
// Motion
let np = vadd(robo_pos, v);
for (const actor of actors) {
actor.update_physics();
}
// TODO: Collide.
robo_pos = np;
robo_vel = v;
// TODO: Bonks
// TODO: Transients
}
// GARBAGE OBVIOUSLY
const robo_info = {
width: 32,
height: 32,
animations: [
{ start: 0, length: 1, speed: 20 },
{ start: 1, length: 4, speed: 8 },
],
};
export function draw() {
cls(0.1, 0.2, 0.3);
if (!loaded) {
return;
}
if (level != undefined) {
draw_level(level, 0, 0);
}
if (bot_sprite != undefined) {
// ...it gets resolved here?
use_texture(bot_sprite);
const moving = robo_vel.x != 0 || robo_vel.y != 0;
const anim = robo_info.animations[moving ? 1 : 0];
const frame = (anim.start + ((clock / anim.speed) % anim.length)) >> 0;
spr(robo_pos.x, robo_pos.y, 32, 32, frame * robo_info.width, 0, 32, 32);
for (const actor of actors) {
actor.draw(clock);
}
// print("FRAME TIME:", since_last_frame());
}