Skip to content

Commit

Permalink
Add entity factories, remove lots of missing implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
eonarheim committed Dec 20, 2023
1 parent 23726e1 commit e829fbe
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 66 deletions.
4 changes: 2 additions & 2 deletions example/example-city.tmx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="100" height="100" tilewidth="16" tileheight="16" infinite="0" nextlayerid="15" nextobjectid="40">
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="100" height="100" tilewidth="16" tileheight="16" infinite="0" nextlayerid="15" nextobjectid="42">
<editorsettings>
<export target="example-city.tmj" format="json"/>
</editorsettings>
Expand Down Expand Up @@ -1282,7 +1282,7 @@
<properties>
<property name="excalibur" type="bool" value="true"/>
</properties>
<object id="6" name="player-start" type="player-start" x="375" y="104">
<object id="6" name="Player" type="player-start" x="375" y="104">
<point/>
</object>
<object id="7" name="Camera" type="Camera" x="444.5" y="404.5">
Expand Down
33 changes: 32 additions & 1 deletion example/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,25 @@ import { TiledMapResource } from '@excalibur-tiled';
import { ImageFiltering, ImageSource, Input, IsometricEntityComponent, Shape } from 'excalibur';
import { TiledResource } from '../src/resource/tiled-resource';

class Player extends ex.Actor {
override onPostUpdate(engine: ex.Engine) {
this.vel = ex.vec(0, 0)
const speed = 64;
if (engine.input.keyboard.isHeld(ex.Keys.Right)) {
this.vel.x = speed;
}
if (engine.input.keyboard.isHeld(ex.Keys.Left)) {
this.vel.x = -speed;
}
if (game.input.keyboard.isHeld(ex.Input.Keys.Up)) {
this.vel.y = -speed;
}
if (game.input.keyboard.isHeld(ex.Input.Keys.Down)) {
this.vel.y = speed;
}
}
}

const game = new ex.Engine({
width: 800,
height: 600,
Expand All @@ -11,7 +30,19 @@ const game = new ex.Engine({
antialiasing: false
});

const newResource = new TiledResource('./example-city.tmx');
const newResource = new TiledResource('./example-city.tmx', {
entityClassNameFactories: {
'player-start': (props) => {
return new Player({
pos: props.worldPos,
width: 16,
height: 16,
color: ex.Color.Blue,
collisionType: ex.CollisionType.Active
});
}
}
});
const loader = new ex.Loader([newResource]);

let currentPointer!: ex.Vector;
Expand Down
2 changes: 1 addition & 1 deletion src/parser/tiled-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export const TiledText = z.object({
const TiledObject = z.object({
id: z.number().optional(), // Template files might not have an id for some reason
name: z.string().optional(),
type: z.string(),
type: z.string().optional(),
x: z.number(),
y: z.number(),
rotation: z.number().optional(),
Expand Down
133 changes: 89 additions & 44 deletions src/resource/layer.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Actor, Color, ParallaxComponent, Polygon as ExPolygon, Shape, TileMap, Tile as ExTile, Vector, toRadians, vec, GraphicsComponent, CompositeCollider } from "excalibur";
import { Actor, Color, ParallaxComponent, Polygon as ExPolygon, Shape, TileMap, Tile as ExTile, Vector, toRadians, vec, GraphicsComponent, CompositeCollider, Entity } from "excalibur";
import { Properties, mapProps } from "./properties";
import { TiledMap, TiledObjectGroup, TiledObjectLayer, TiledTileLayer, isCSV, needsDecoding } from "../parser/tiled-parser";
import { Decoder } from "./decoder";
import { TiledResource } from "./tiled-resource";
import { FactoryProps, TiledResource } from "./tiled-resource";
import { Ellipse, InsertedTile, PluginObject, Point, Polygon, Polyline, Rectangle, Text, parseObjects } from "./objects";
import { getCanonicalGid } from "./gid-util";
import { Tile } from "./tileset";
import { TiledDataComponent } from "./tiled-data-component";
import { satisfies } from "compare-versions";

export type LayerTypes = ObjectLayer | TileLayer;

Expand All @@ -19,60 +21,105 @@ export interface Layer extends Properties {
export class ObjectLayer implements Layer {
public readonly name: string;
properties = new Map<string, string | number | boolean>();
objects: Object[] = [];
actors: Actor[] = [];
objectToActor = new Map<Object, Actor>();
objects: PluginObject[] = [];
entities: Entity[] = [];
private _objectToActor = new Map<PluginObject, Entity>();
private _actorToObject = new Map<Entity, PluginObject>();
constructor(public tiledObjectLayer: TiledObjectLayer, public resource: TiledResource) {
this.name = tiledObjectLayer.name;

mapProps(this, tiledObjectLayer.properties);
}

_hasWidthHeight(object: PluginObject) {
return object instanceof Rectangle || object instanceof InsertedTile
return object instanceof Rectangle || object instanceof InsertedTile;
}

getObjectByName(): PluginObject[] {
// TODO
return [];
getObjectByName(name: string): PluginObject[] {
return this.objects.filter(o => o.tiledObject.name === name);
}

getActorByName(): PluginObject[] {
// TODO
return [];
getEntityByName(name: string): Entity[] {
return this.entities.filter(a => a.name === name);
}

getObjectByProperty(): PluginObject[] {
// TODO
return [];

getEntityByObject(object: PluginObject): Entity | undefined {
return this._objectToActor.get(object);
}
getActorByProperty(): PluginObject[] {
// TODO
return [];

getObjectByEntity(actor: Entity): PluginObject | undefined {
return this._actorToObject.get(actor);
}

/**
* Search for a tiled object that has a property name, and optionally specify a value
* @param propertyName
* @param value
* @returns
*/
getObjectsByProperty(propertyName: string, value?: any): PluginObject[] {
if (value !== undefined) {
return this.objects.filter(o => o.properties.get(propertyName) === value);
} else {
return this.objects.filter(o => o.properties.has(propertyName));
}
}
/**
* Search for actors that were created from tiled objects
* @returns
*/
getActorsByProperty(propertyName: string, value?: any): Actor[] {
return this.getObjectsByProperty(propertyName, value).map(o => this._objectToActor.get(o)).filter(a => !!a) as Actor[];
}

getObjectByClassName(): PluginObject[] {
return [];
/**
* Search for an Tiled object by it's Tiled class name
* @returns
*/
getObjectsByClassName(className: string): PluginObject[] {
return this.objects.filter(o => o.tiledObject.name === className);
}

getActorByClassName(): PluginObject[] {
return [];
/**
* Search for an Actor created by the plugin by it's Tiled object
* @param className
* @returns
*/
getActorByClassName(className: string): Actor[] {
return this.getObjectsByClassName(className).map(o => this._objectToActor.get(o)).filter(a => !!a) as Actor[];
}

async load() {
// TODO object alignment specified in tileset! https://doc.mapeditor.org/en/stable/manual/objects/#insert-tile
const opacity = this.tiledObjectLayer.opacity;
const hasTint = !!this.tiledObjectLayer.tintcolor;
const tint = this.tiledObjectLayer.tintcolor ? Color.fromHex(this.tiledObjectLayer.tintcolor) : Color.White;
const offset = vec(this.tiledObjectLayer.offsetx ?? 0, this.tiledObjectLayer.offsety ?? 0);
// TODO object alignment specified in tileset! https://doc.mapeditor.org/en/stable/manual/objects/#insert-tile


// TODO factory instantiation!

const objects = parseObjects(this.tiledObjectLayer);

for (let object of objects) {
let worldPos = vec((object.x ?? 0) + offset.x, (object.y ?? 0) + offset.y);

if (object.tiledObject.type) {
// TODO we should also use factories on templates
const factory = this.resource.factories.get(object.tiledObject.type);
if (factory) {
const entity = factory({
worldPos,
name: object.tiledObject.name,
class: object.tiledObject.type,
layer: this,
object,
properties: object.properties
} satisfies FactoryProps);
this._recordObjectEntityMapping(object, entity);
continue; // If we do a factor method we skip any default processing
}
}

// TODO excalibur smarts for solid/collision type/factory map
// TODO collision type
const newActor = new Actor({
name: object.tiledObject.name,
x: (object.x ?? 0) + offset.x,
Expand All @@ -84,7 +131,6 @@ export class ObjectLayer implements Layer {
if (graphics) {
graphics.opacity = opacity;
}


if (object instanceof Text) {
newActor.graphics.use(object.text);
Expand Down Expand Up @@ -144,12 +190,11 @@ export class ObjectLayer implements Layer {
}

if (object instanceof Polyline) {
console.log('polyline', object);
// TODO should we do any actor stuff here
// ? should we do any excalibur things here
}

if (object instanceof Point) {
// TODO should we do any actor stuff here
// ? should we do any excalibur things here
}

if (object instanceof Rectangle) {
Expand All @@ -164,16 +209,23 @@ export class ObjectLayer implements Layer {
console.log(object);
}

this.objects.push(object);
this.actors.push(newActor);
// TODO do we need this?
this.objectToActor.set(object, newActor);
this._recordObjectEntityMapping(object, newActor);
}
}

private _recordObjectEntityMapping(object: PluginObject, entity: Entity){
entity.addComponent(new TiledDataComponent({
tiledObject: object
}));
this.objects.push(object);
this.entities.push(entity);
this._objectToActor.set(object, entity);
this._actorToObject.set(entity, object);
}
}

/**
* Tile information for both excalibur and tiled tile represenations
* Tile information for both excalibur and tiled tile representations
*/
export interface TileInfo {
/**
Expand Down Expand Up @@ -208,6 +260,7 @@ export class TileLayer implements Layer {
*/
tilemap!: TileMap;


getTileByPoint(worldPos: Vector): TileInfo | null {
// TODO IF the resource is not loaded & decoded THIS WONT WORK
// log a warning
Expand Down Expand Up @@ -248,14 +301,6 @@ export class TileLayer implements Layer {
return null;
}

getTileByClass() {
// TODO implement getTileByClass
}

getTileByProperty() {
// TODO implement getTileByProperty
}

constructor(public tiledTileLayer: TiledTileLayer, public resource: TiledResource) {
this.name = tiledTileLayer.name;
mapProps(this, tiledTileLayer.properties);
Expand Down
17 changes: 17 additions & 0 deletions src/resource/tiled-data-component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Component } from "excalibur";
import { PluginObject } from "./objects";
import { Properties } from "./properties";

export interface TiledDataComponentOptions {
tiledObject: PluginObject;
// tiledProperties: Properties;
}
export class TiledDataComponent extends Component<'ex.tiled-data'> {
public readonly type = 'ex.tiled-data';
public tiledObject: PluginObject;
constructor(options: TiledDataComponentOptions){
super();
const {tiledObject} = options;
this.tiledObject = tiledObject;
}
}
Loading

0 comments on commit e829fbe

Please sign in to comment.