Skip to content

Commit

Permalink
tilesset loader and scene setup
Browse files Browse the repository at this point in the history
  • Loading branch information
codescapade committed Sep 16, 2024
1 parent ff2dc1a commit 1cdc876
Show file tree
Hide file tree
Showing 6 changed files with 349 additions and 23 deletions.
110 changes: 89 additions & 21 deletions src/jume/Jume.hx
Original file line number Diff line number Diff line change
@@ -1,29 +1,37 @@
package jume;

import jume.events.SceneEvent;
import jume.math.Random;
import jume.audio.Audio;
import jume.graphics.RenderTarget;
import jume.view.View;
import jume.utils.TimeStep;
import jume.input.Input;
import jume.graphics.Graphics;
import jume.graphics.gl.Context;
import jume.ecs.Scene;

import haxe.Timer;

import jume.events.FocusEvent;
import jume.di.Services;

import haxe.Exception;

import js.html.CanvasElement;
import js.Browser;

import jume.utils.BrowserInfo.isMobile;
import jume.events.Events;
import jume.JumeOptions.setDefaultJumeOptions;
import jume.assets.Assets;
import jume.assets.AtlasLoader;
import jume.assets.BitmapFontLoader;
import jume.assets.ImageLoader;
import jume.assets.ShaderLoader;
import jume.assets.SoundLoader;
import jume.assets.TextLoader;
import jume.assets.TilesetLoader;
import jume.audio.Audio;
import jume.di.Services;
import jume.ecs.Scene;
import jume.events.Events;
import jume.events.FocusEvent;
import jume.events.ResizeEvent;
import jume.events.SceneEvent;
import jume.graphics.Color;
import jume.graphics.Graphics;
import jume.graphics.RenderTarget;
import jume.graphics.gl.Context;
import jume.input.Input;
import jume.math.Mat4;
import jume.math.Random;
import jume.math.Size;
import jume.math.Vec2;
import jume.utils.BrowserInfo.isMobile;
import jume.utils.TimeStep;
import jume.view.View;

class Jume {
/**
Expand Down Expand Up @@ -65,6 +73,8 @@ class Jume {

var scene: Scene;

var tempPos: Vec2;

/**
* Create a new Jume instance.
* @param options The game options.
Expand Down Expand Up @@ -105,6 +115,8 @@ class Jume {
context = new Context(options.canvasId, options.forceWebGL1);
Services.add(context);

tempPos = new Vec2();

view = new View({
width: options.designSize.widthi,
height: options.designSize.heighti,
Expand All @@ -127,6 +139,12 @@ class Jume {

graphics = new Graphics(context, view);

final assets = new Assets();
addAssetLoaders(assets);

target = new RenderTarget(new Size(view.viewWidth, view.viewHeight), view.pixelFilter ? NEAREST : LINEAR,
view.pixelFilter ? NEAREST : LINEAR);

#if !headless
canvas.focus();
canvas.addEventListener('blur', () -> lostFocus());
Expand All @@ -153,14 +171,42 @@ class Jume {
public function hasFocus() {
inFocus = true;
FocusEvent.send(FocusEvent.HAS_FOCUS);
scene.hasFocus();
}

public function lostFocus() {
inFocus = false;
FocusEvent.send(FocusEvent.LOST_FOCUS);
scene.lostFocus();
}

function addAssetLoaders(assets: Assets) {
assets.registerLoader(new AtlasLoader());
assets.registerLoader(new BitmapFontLoader());
assets.registerLoader(new ImageLoader());
assets.registerLoader(new ShaderLoader());
assets.registerLoader(new SoundLoader());
assets.registerLoader(new TextLoader());
assets.registerLoader(new TilesetLoader());
}

function resize(width: Int, height: Int) {}
function resize(width: Int, height: Int) {
final ratio = view.pixelRatio;
if (view.isFullScreen) {
final canvas = view.canvas;
canvas.width = width * ratio;
canvas.height = height * ratio;
canvas.style.width = '${width}px';
canvas.style.height = '${height}px';
view.scaleToFit();

target = new RenderTarget(new Size(view.viewWidth, view.viewHeight), view.pixelFilter ? NEAREST : LINEAR,
view.pixelFilter ? NEAREST : LINEAR);
}

ResizeEvent.send(ResizeEvent.RESIZE, width * ratio, height * ratio);
scene.resize(width * ratio, height * ratio);
}

function headlessLoop() {
final now = Timer.stamp();
Expand All @@ -184,13 +230,35 @@ class Jume {
dt = MAX_DT;
}

timeStep.update(dt);
scene.update(timeStep.dt);

#if !headless
render();
#end
}
}

function render() {}
function render() {
graphics.transform.identity();
graphics.pushTarget(target);

scene.render(graphics);

graphics.popTarget();

// TOdO: Draw fps and memory here.

graphics.transform.identity();
graphics.color.copyFrom(Color.WHITE);

Mat4.fromScale(view.viewScaleX, view.viewScaleY, 1, graphics.transform);

graphics.start();
tempPos.set(0, 0);
graphics.drawRenderTarget(tempPos, target);
graphics.present();
}

function onSceneChange(event: SceneEvent) {
changeScene(event.sceneType);
Expand Down
69 changes: 69 additions & 0 deletions src/jume/assets/TilesetLoader.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package jume.assets;

import jume.graphics.Image;

import haxe.Exception;

import jume.tilemap.Tileset;

typedef LoadTilesetProps = {
var tileWidth: Int;
var tileHeight: Int;
var spacing: Int;
var margin: Int;
}

class TilesetLoader extends AssetLoader<Tileset> {
@:inject
var assets: Assets;

public function new() {
super(Tileset);
}

public override function load(id: String, path: String, callback: (asset: Tileset)->Void, ?props: Dynamic,
?keep: Bool) {
keep ??= true;

if (props == null
|| props.tileWidth == null
|| props.tileHeight == null
|| props.spacing == null
|| props.margin == null) {
throw new Exception('Missing properties needed to load the tileset.');
}

final tilesetProps: LoadTilesetProps = cast props;

assets.load({
assetType: Image,
id: 'jume_tileset_${id}',
path: path,
keep: keep,
callback: (image) -> {
if (image != null) {
final tileset = new Tileset(image, tilesetProps.tileWidth, tilesetProps.tileHeight, tilesetProps.spacing,
tilesetProps.margin);

if (keep) {
loadedAssets[id] = tileset;
}

callback(tileset);
} else {
callback(null);
}
}
});
}

public override function unload(id: String): Bool {
if (loadedAssets.exists(id)) {
assets.unload(Image, 'jume_tileset_${id}');

return super.unload(id);
}

return false;
}
}
2 changes: 1 addition & 1 deletion src/jume/ecs/Scene.hx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class Scene {
systems.render(graphics);
}

public function resize() {
public function resize(width: Int, height: Int) {
for (camera in cameras) {
camera.resize();
}
Expand Down
143 changes: 143 additions & 0 deletions src/jume/tilemap/TilemapColliders.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package jume.tilemap;

import jume.math.Vec2;
import jume.math.Rectangle;

typedef CollisionTile = {
var id: Int;
var checked: Bool;
};

typedef GenFromIntGridProps = {
var grid: Array<Array<Int>>;
var worldX: Int;
var worldY: Int;
var tileWidth: Int;
var tileHeight: Int;
var collisionIds: Array<Int>;
};

typedef GenColliderProps = {
var tiles: Array<Array<CollisionTile>>;
var worldX: Int;
var worldY: Int;
var tileWidth: Int;
var tileHeight: Int;
var collisionIds: Array<Int>;
};

function generateFromIntGrid(params: GenFromIntGridProps): Array<Rectangle> {
final tiles: Array<Array<CollisionTile>> = [];

for (y in 0...params.grid.length) {
final row: Array<CollisionTile> = [];
for (x in 0...params.grid[0].length) {
row.push({ id: params.grid[y][x], checked: false });
}
tiles.push(row);
}

return generateColliders({
tiles: tiles,
worldX: params.worldX,
worldY: params.worldY,
tileWidth: params.tileWidth,
tileHeight: params.tileHeight,
collisionIds: params.collisionIds
});
}

function isCollisionTile(id: Int, collisionIds: Array<Int>): Bool {
// If no ids specified every non-empty tile is a collision tile.
if (collisionIds.length == 0) {
return id != -1;
}

return collisionIds.contains(id);
}

function generateColliders(params: GenColliderProps): Array<Rectangle> {
final colliders: Array<Rectangle> = [];
final start = Vec2.get();
final current = Vec2.get();

var checking = false;
var foundLastY = false;

// Starting at the top lef, loop over all tiles and create colliders.
for (x in 0...params.tiles[0].length) {
for (y in 0...params.tiles.length) {
var tile = params.tiles[y][x];

// Check if the tile should be part of a collider.
if (tile.checked || !isCollisionTile(tile.id, params.collisionIds)) {
continue;
}

tile.checked = true;
start.set(x, y);
current.set(x, y);
checking = true;
foundLastY = false;

// Move down until there is no collider found or the3nd of the map is reached.
while (checking) {
// If it found the bottom most connected collision tile move right from the start to see how big
// the collider can be horizontally.
if (foundLastY) {
current.x++;
if (current.x >= params.tiles[0].length) {
checking = false;
current.x--;
break;
}

for (i in start.yi...(current.yi + 1)) {
tile = params.tiles[i][current.xi];
if (tile.checked || !isCollisionTile(tile.id, params.collisionIds)) {
current.x--;
checking = false;
} else {
tile.checked = true;
}

if (!checking) {
break;
}
}

if (!checking) {
for (i in start.yi...(current.yi + 1)) {
params.tiles[i][current.xi + 1].checked = false;
}
}
} else {
current.y++;
if (current.y >= params.tiles.length) {
foundLastY = true;
current.y--;
} else {
tile = params.tiles[current.yi][current.xi];
if (tile.checked || !isCollisionTile(tile.id, params.collisionIds)) {
current.y--;
foundLastY = true;
} else {
tile.checked = true;
}
}
}
}

final distX = current.x - start.x + 1;
final distY = current.y - start.x + 1;
final xPos = params.worldX + start.x * params.tileWidth;
final yPos = params.worldY + start.y * params.tileHeight;
colliders.push(new Rectangle(xPos, yPos, params.tileWidth * distX, params.tileHeight * distY));
}
}

start.put();
current.put();

return colliders;
}
Loading

0 comments on commit 1cdc876

Please sign in to comment.