Full city: ok

This commit is contained in:
Thibault Neveu committed 2018-06-08 11:32:49 +01:00
1 parent 6999a8532e
commit 3b5eaf35a8
24 files changed
+1535 -102

No files matched your search

+27 -17
View File
@@ -14,6 +14,7 @@ import {CarOptions, Car} from "./car";
import {BasicMotionEngine} from "./basic_motion_engine";
import {ControlMotionEngine} from "./control_motion_engine";
import {BotMotionEngine} from "./bot_motion_engine";
import { Editor } from "./editor";
export interface AssetInfo {
readonly mx?: number;
@@ -38,6 +39,7 @@ export interface RoadSprite extends PIXI.Sprite {
// (x, y position) Relatif to the map
mx?: number;
my?: number;
isremove?: boolean;
}
export interface SimpleSprite extends PIXI.Sprite {
@@ -45,17 +47,18 @@ export interface SimpleSprite extends PIXI.Sprite {
// name of the asset (not the name of the image)
type?: string;
mapId?: number;
isremove?: boolean;
}
export class AssetManger {
private level: Level;
private level: Level|Editor;
// Used to list the differents MotionEngine possible to add on the car
private motion: any;
// List of all items on the map
private assets: SimpleSprite[] = [];
public assets: SimpleSprite[] = [];
constructor(level: Level) {
constructor(level: Level|Editor) {
this.level = level;
this.motion = {
"BasicMotionEngine": BasicMotionEngine,
@@ -111,7 +114,8 @@ export class AssetManger {
*/
if (line == undefined)
line = 0;
if (road.cars.length >= 1 && this.level.findCarById(road.cars[0]).core.line == line){
let carById = this.level.findCarById(road.cars[0]);
if (road.cars.length >= 1 && carById && carById.core.line == line){
line = line == 0 ? 1:0;
}
if (road.cars.length >= 2){
@@ -129,10 +133,10 @@ export class AssetManger {
let y_margin = (ROADSIZE*1/4);
// transform to map x coordinate
// transform to map y coordinate
road.orientation = (Math.floor(road.orientation / car.rotation_step)*car.rotation_step);
let theta = -road.orientation*(Math.PI);
let x_m = (Math.cos(theta) * x_margin) - (y_margin * Math.sin(theta));
let y_m = (Math.cos(theta) * y_margin) + (x_margin * Math.sin(theta));
road.orientation = (Math.floor(road.orientation / car.rotationStep)*car.rotationStep);
let th = -road.orientation*(Math.PI); // theta
let x_m = (Math.cos(th) * x_margin) - (y_margin * Math.sin(th));
let y_m = (Math.cos(th) * y_margin) + (x_margin * Math.sin(th));
let line_side_factor = line == 0 ? 1:-1;
let line_side_factor_t = line == 0 ? 0:Math.PI;
@@ -143,7 +147,7 @@ export class AssetManger {
car.mx = Math.floor(car.x / ROADSIZE);
car.my = Math.floor(car.y / ROADSIZE);
// Car rotation
car.rotation = (theta+line_side_factor_t);
car.rotation = (th+line_side_factor_t);
// Set the new road of the car
car.checkAndsetNewRoad(road);
}
@@ -169,7 +173,7 @@ export class AssetManger {
this.assets.push(asset);
}
createMap(map: (string|number)[][], info: LevelInfo, textures: PIXI.Texture, roadside:boolean=true){
createMap(map: (string|number)[][], info: LevelInfo, textures: (PIXI.Texture|PIXI.loaders.TextureDictionary), roadside:boolean=true){
/*
Method used to create the map with all assets (except the cars)
@map (2dim Array)
@@ -204,7 +208,7 @@ export class AssetManger {
}
}
createCars(map: (string|number)[][], info: LevelInfo, textures: PIXI.Texture){
createCars(map: (string|number)[][], info: any, textures: (PIXI.Texture|PIXI.loaders.TextureDictionary)){
/*
Method used to create all the cars
@map (2dim Array)
@@ -213,7 +217,7 @@ export class AssetManger {
*/
// Go through all the bot cars on the map
for (let c in info.cars){
let options: CarOptions = {lidar: false, lidarInfo: {pts: 2, width: 0.5, height: 1, pos: 1}};
let options: CarOptions = {lidar: true, lidarInfo: {pts: 2, width: 0.5, height: 1, pos: 1}};
if (info.cars[c].auto){
options.lidar = true;
options.motionEngine = new BotMotionEngine(this.level);
@@ -221,10 +225,11 @@ export class AssetManger {
let n_car = new Car(this.level, info.cars[c], textures, options);
// Append the car to the canvas
this.level.addCar(n_car);
//this.level.addChild(n_car.lidar);
}
}
createAgent(map: (string|number)[][], info: LevelInfo, textures: PIXI.Texture){
createAgent(map: (string|number)[][], info: any, textures: (PIXI.Texture|PIXI.loaders.TextureDictionary)){
/*
Method used to create the agent's car.
@map (2dim Array)
@@ -244,6 +249,10 @@ export class AssetManger {
return agent;
}
addAsset(asset: any){
this.assets.push(asset);
}
exportMap(width: number, height: number, file_name: string){
/*
Export the map
@@ -251,8 +260,9 @@ export class AssetManger {
@height (Integer) Height of the new map
@file_name (String) name of the file to export
*/
var file = {"cars": []};
var file: any = {"cars": []};
let map = [];
let envs = this.level.getEnvs();
for (var y = 0; y < height; y++) {
let line = [];
for (var x = 0; x < width; x++) {
@@ -260,8 +270,8 @@ export class AssetManger {
}
map.push(line);
}
for (var e = 0; e < this.level.envs.length; e++) {
let elem = this.level.envs[e];
for (var e = 0; e < envs.length; e++) {
let elem = envs[e];
if (!elem.isremove){
// Add the cars
if (elem.mapId == MAP.CAR && !elem.agent && elem.is_valid){
@@ -300,7 +310,7 @@ export class AssetManger {
"motion": {
"type": "BasicMotionEngine",
"options":{
"rotation_step": 0.5,
"rotationStep": 0.5,
"actions": ["UP", "LEFT", "RIGHT", "DOWN", "WAIT"]
}
}
+17 -11
View File
@@ -4,6 +4,7 @@ import {Level} from "./level";
import {
MAP, ROADSIZE
} from "./global";
import { Editor } from "./editor";
export class BotMotionEngine extends MotionEngine {
/*
@@ -18,7 +19,7 @@ export class BotMotionEngine extends MotionEngine {
private map: (string|number)[][];
private rotationToNextCase: any;
constructor(level: Level) {
constructor(level: Level|Editor) {
super(level);
this.map = this. level.getMap();
@@ -35,7 +36,7 @@ export class BotMotionEngine extends MotionEngine {
this.rotationToNextCase[-0.5] = {"mx": 0, "my": -1};
this.rotationToNextCase[-1.0] = {"mx": -1, "my": 0};
this.rotationToNextCase[-1.5] = {"mx": 0, "my": 1};
// this.rotationToNextCase[null] = {"mx": 0, "my": 0};
//this.rotationToNextCase[undefined] = {"mx": 0, "my": 0};
}
setUp(car: any, lidar: any){
@@ -93,7 +94,7 @@ export class BotMotionEngine extends MotionEngine {
Method used to check is there a road is present at this potion
on the map
*/
if (ny < 0 || ny >= this.mapSizeY || nx < 0 || nx >= this.mapSizeX || this.level.map[ny][nx] == 0){
if (ny < 0 || ny >= this.mapSizeY || nx < 0 || nx >= this.mapSizeX || this.map[ny][nx] == 0){
return false;
}
return true;
@@ -114,7 +115,7 @@ export class BotMotionEngine extends MotionEngine {
if (this.car.next_road.rotation_type == 0){ // Turn right
if (y_dist < right_dist && x_dist < right_dist){
this.car.have_turned = true;
this.car.haveTurned = true;
this.car.rotation = this.car.rotation += Math.PI/2;
this.car.rotation = this.car.rotation % (2*Math.PI);
this.car.next_road = undefined;
@@ -124,7 +125,7 @@ export class BotMotionEngine extends MotionEngine {
if (this.car.next_road.rotation_type == 1){ // Turn left
if (y_dist < left_dist && x_dist < left_dist){
this.car.have_turned = true;
this.car.haveTurned = true;
this.car.rotation = this.car.rotation -= Math.PI/2;
this.car.rotation = this.car.rotation % (2*Math.PI);
this.car.next_road = undefined;
@@ -134,7 +135,7 @@ export class BotMotionEngine extends MotionEngine {
if (this.car.next_road.rotation_type == 2){ // Rotation
if (y_dist < same_dist && x_dist < same_dist){
this.car.have_turned = true;
this.car.haveTurned = true;
this.car.rotation = this.car.rotation -= Math.PI;
this.car.rotation = this.car.rotation % (2*Math.PI);
@@ -176,19 +177,24 @@ export class BotMotionEngine extends MotionEngine {
if (!this.isRoad(nx, ny)){
turn = true;
}
else if (!this.car.have_turned && !this.car.turn_random){
this.car.turn_random = Math.random();
else if (!this.car.haveTurned && !this.car.turnedRandom){
this.car.turnedRandom = Math.random();
}
else if(!this.car.have_turned && this.car.turn_random < 0.33 && this.isRoad(this.car.mx + is_next_pos_r.mx, this.car.my + is_next_pos_r.my)){
else if(!this.car.haveTurned && this.car.turnedRandom < 0.33 && this.isRoad(this.car.mx + is_next_pos_r.mx, this.car.my + is_next_pos_r.my)){
turn = true;
}
else if(!this.car.have_turned && this.car.turn_random > 0.66 && this.isRoad(this.car.mx + is_next_pos_l.mx, this.car.my + is_next_pos_l.my)){
else if(!this.car.haveTurned && this.car.turnedRandom > 0.66 && this.isRoad(this.car.mx + is_next_pos_l.mx, this.car.my + is_next_pos_l.my)){
turn = true;
}
if (turn){
let possibles_rotations = [(key+0.5) % 2., (key-0.5) % 2., null];
for (let p = 0; p < possibles_rotations.length; p++){
let next_pos = this.rotationToNextCase[possibles_rotations[p]];
let next_pos;
if (possibles_rotations[p] != null){
next_pos = this.rotationToNextCase[possibles_rotations[p]];
} else{
next_pos = {"mx": 0, "my": 0};
}
let px = this.car.mx + next_pos.mx;
let py = this.car.my + next_pos.my;
if (this.isRoad(px, py)){
+16 -8
View File
@@ -4,7 +4,8 @@
This class create the agent and the associated sensor (lidar)
*/
import {Level, LevelInfo} from "./level";
import {Level} from "./level";
import {Editor} from "./editor";
import {
CAR_IMG, Sprite, MAP, ROADSIZE, Container, Graphics
@@ -49,17 +50,22 @@ export interface CarSprite extends PIXI.Sprite {
agent?: boolean;
}
export interface LidarChild extends PIXI.Graphics {
// Is a lidar point
pt?: boolean;
}
export class Car {
public level: Level;
public level: Level|Editor;
public core: CarSprite;
public lidar: any;
private info: CarInfo;
private motion: any;
private turnedRandom: any;
public turnedRandom: any;
constructor(level: Level, info: CarInfo, textures: any, options:CarOptions={}) {
constructor(level: Level|Editor, info: CarInfo, textures: any, options:CarOptions={}) {
/*
@level (Level class)
@info: (CarInfo) Info from the json file about the car
@@ -200,9 +206,9 @@ export class Car {
@height: Height of the lidar (in proportion to the car)
*/
this.lidar = new Container();
// Area of the lidar
let area = new Graphics();
let area: LidarChild = new Graphics();
area.pt = false;
area.alpha = 0.5;
area.beginFill(0x515151);
area.drawRect(0, 0, this.core.width*lidarOptions.width, this.core.height*lidarOptions.height);
@@ -216,8 +222,8 @@ export class Car {
let x = (x_step/4) + xs * x_step;
let y = (y_step/4) + ys * y_step;
let pt = new Graphics();
let pt: LidarChild = new Graphics();
pt.pt = true;
pt.beginFill(0xffffff);
pt.drawRect(x, y, 5, 5);
pt.endFill();
@@ -227,8 +233,10 @@ export class Car {
this.lidar.pts = lidarOptions.pts;
this.lidar.addChild(area);
this.lidar.x = this.core.x;
this.lidar.y = this.core.y;
this.lidar.pivot.y = this.lidar.height/2;
this.lidar.pivot.x = this.core.width/2 - (lidarOptions.pos*this.core.width);
this.lidar.rotation = this.core.rotation;
+332
View File
@@ -0,0 +1,332 @@
import { LevelInfo, Roads } from "./level";
import { AssetManger, RoadSprite } from "./asset_manager";
import { Car} from "./car";
import {Loader, Sprite, JSON_TEXTURES, ROADSIZE, MAP, LEVEL_FOLDER} from "./global";
import * as U from "./utils";
/*
@Level class
This is the core of game, the class is used create all the differents
services in the game (assets, map, agents...).
*/
export class Editor {
public app: PIXI.Application = null; // The pixi app.
// Informations on the level
private levelName: string;
private info: LevelInfo = null;
private envs: any[] = [] // Used to list all elements the car can crash with
public am: AssetManger;
public map: (string|number)[][] = null; // Use the roads positions of the level
public agent: any = null; // (@Car class) for the agent
// Object to store all the roads assets
// Each road is accesible as follow this.roads[[my, mx]]
// with my and mx the (x, y) position relative to the map (not pixel).
private cars: Car[] = []; // Array used to store all the cars assets
private loop: any = null; // Loop method called for each render
// Current selector items by the editor
public selectedItems: any = null;
private canvasId: string; // Id of the target canvas
private roads: Roads = {};
constructor(levelName: string, canvasId: string) {
/*
@levelName (String) Name of the level to load (.json)
*/
this.levelName = levelName;
this.canvasId = canvasId;
this.am = new AssetManger(this);
}
selectedItem(item: any){
/*
Method to select an items.
Once an item is selected, the item stick to the current
mouse position
@items (Object)
@img (String)
@type (String)
*/
if(this.selectedItems){
this.app.stage.removeChild(this.selectedItems);
}
let textures = Loader.resources[JSON_TEXTURES].textures;
this.selectedItems = new Sprite(textures[item.image]);
this.selectedItems.type = item.type;
this.selectedItems.image = item.image;
this.selectedItems.data = item.data;
// The road is not an obstacle
this.selectedItems.x = 0;
this.selectedItems.y = 0;
this.app.stage.addChild(this.selectedItems);
}
removeItem(item: any){
if (item.lidar){
this.app.stage.removeChild(item.lidar);
}
if (item.map_id == MAP.CAR){
item.road.cars.splice(item.road.cars.indexOf(item.car_id), 1);
}
if (item.agent)
this.agent = undefined;
this.app.stage.removeChild(item);
item.isremove = true;
}
setCurrentItem(){
this.am.addAsset(this.selectedItems);
let textures = Loader.resources[JSON_TEXTURES].textures;
var that = this;
if (this.selectedItems.type == "road"){
this.am.createRoad({
mx: Math.floor(this.selectedItems.x / ROADSIZE),
my: Math.floor(this.selectedItems.y / ROADSIZE),
type: this.selectedItems.data
}, textures);
this.envs[this.envs.length - 1].interactive = true;
this.envs[this.envs.length - 1].on("rightclick", function(this: any) {that.removeItem(this)});
}
else if (this.selectedItems.type == "car"){
this.am.createCars(this.map, {cars:[{
"mx": Math.floor(this.selectedItems.x / ROADSIZE),
"my": Math.floor(this.selectedItems.y / ROADSIZE),
"rotation": 0,
"line": 0,
}]}, textures);
this.envs[this.envs.length - 1].interactive = true;
this.envs[this.envs.length - 1].on("rightclick", function(this: any) {that.removeItem(this)});
}
else if (this.selectedItems.type == "agent" && this.agent == undefined){
this.agent = this.am.createAgent(this.map, {agent:{
"mx": Math.floor(this.selectedItems.x / ROADSIZE),
"my": Math.floor(this.selectedItems.y / ROADSIZE),
"rotation": 0,
"line": 0,
"motion": {
"type": "BasicMotionEngine",
"options":{
"rotation_step": 0.5,
"actions": ["UP", "LEFT", "RIGHT", "DOWN", "WAIT"]
}
}
}}, textures);
this.envs[this.envs.length - 1].interactive = true;
this.envs[this.envs.length - 1].on("rightclick", function(this: any) {that.removeItem(this)});
}
else if (this.selectedItems.type == "asset"){
this.am.createAsset(this.selectedItems.image, {x: this.selectedItems.x, y: this.selectedItems.y} ,textures, this.selectedItems.data);
this.am.assets[this.am.assets.length - 1].interactive = true;
this.am.assets[this.am.assets.length - 1].on("rightclick", function(this: any) {that.removeItem(this)});
}
this.app.stage.removeChild(this.selectedItems);
this.selectedItems = null;
}
load(loop: any){
/*
Load the map from the file given in the constructor of the class
@loop (Method) This method will be call for each render
*/
this.loop = loop;
return new Promise((resolve, reject) => {
// Load the level
U.loadJSON(LEVEL_FOLDER + this.levelName, (response: any) => {
// Parse JSON string into object
this.info = JSON.parse(response);
this.createLevel(this.info).then(() => resolve());
});
});
}
createLevel(info: LevelInfo){
/*
Create the level
@info (Object) Level's json.
*/
this.map = info.map; // Store the map to let it accessible faster later on.
//Create the Pixi Application
this.app = new PIXI.Application({
width: this.map[0].length * ROADSIZE,
height: this.map.length * ROADSIZE,
backgroundColor: 0x80bf3e
}
);
// Append the app to the body
document.getElementById(this.canvasId).appendChild(this.app.view);
return new Promise((resolve, reject) => {
Loader.add(["textures/textures.json", "textures/textures.png"]).load(() => {
this.setup(info); // Set up the level (Add assets)
resolve();
});
});
}
setup(info: any){
/*
Setup all the element of the map
@info (Object) Level's json.
*/
// Load the textures file
let textures = Loader.resources[JSON_TEXTURES].textures;
// Load all elements of the car
this.am.createMap(this.map, info, textures, false);
this.am.createCars(this.map, info, textures);
if (info.agent)
this.agent = this.am.createAgent(this.map, info, textures);
var that = this;
for (var i = 0; i < this.envs.length; i++) {
this.envs[i].interactive = true;
this.envs[i].on("rightclick", function(this: any) {that.removeItem(this)});
}
for (var i = 0; i < this.am.assets.length; i++) {
this.am.assets[i].interactive = true;
this.am.assets[i].on("rightclick", function(this: any) {that.removeItem(this)});
}
// Set up the main loop
this.app.ticker.add(delta => this.loop(delta));
}
resize(width: number, height: number){
/*
Resize the map
*/
this.app.renderer.resize(width * ROADSIZE, height * ROADSIZE);
}
addChild(child: any){
/**
* Add child to the app
*/
this.app.stage.addChild(child);
}
addCar(car: Car){
/**
* Add car to the level
*/
this.cars.push(car);
this.app.stage.addChild(car.core);
this.envs.push(car.core);
}
getMap(): (string|number)[][] {
return this.map;
}
getEnvs(): any[] {
/**
* Return the list of envs
* /
*/
return this.envs;
}
addRoad(road: RoadSprite){
/*
Add road using the mx and my positions
*/
this.roads[[road.my.toString(), road.mx.toString()].toString()] = road;
this.envs.push(road);
this.app.stage.addChild(road);
}
findCarById(id: number){
/*
Find car by @id
*/
return this.cars.find((e: any) => {return e.car_id == id});
}
getRoad(my: number, mx: number){
return this.roads[[my.toString(), mx.toString()].toString()];
}
getRoads(){
return this.roads;
}
}
/*
function createEditor(id: string){
// Get the DOM element to change the width/height of the map
const height_input = document.getElementById("height_input");
const width_input = document.getElementById("width_input");
var width = parseInt((<HTMLInputElement>width_input).value);
var height = parseInt((<HTMLInputElement>height_input).value);
// Create the editor class
let file = window.location.hash || "template.json";
file = file.slice(1, file.length)
console.log(file);
var editor = new Editor(file, "string");
// For the first init retrieve the widht and height of the map
var fist_init = false;
function init(){
width = editor.map[0].length;
height = editor.map.length;
(<HTMLInputElement>width_input).value = width.toString();
(<HTMLInputElement>height_input).value = height.toString();
}
function load(){
if (!fist_init){
fist_init = true;
init();
}
let mouse_position = editor.app.renderer.plugins.interaction.mouse.global;
if (editor.selectedItems && editor.selectedItems.type == "asset"){
editor.selectedItems.x = mouse_position.x;
editor.selectedItems.y = mouse_position.y;
}
else if (editor.selectedItems) {
editor.selectedItems.x = Math.floor(mouse_position.x/ROADSIZE) * ROADSIZE;
editor.selectedItems.y = Math.floor(mouse_position.y/ROADSIZE) * ROADSIZE;
}
}
// Game loop
editor.load((delta: any) => {
load();
});
var classname = document.getElementsByClassName("img_item");
for (var i = 0; i < classname.length; i++) {
classname[i].addEventListener('click', function(this: any){
editor.selectedItem({image : this.getAttribute('data-img'), type: this.getAttribute('data-type'), data: this.getAttribute("data-data")});
}, false);
}
width_input.onchange = () => {
width = parseInt((<HTMLInputElement>width_input).value);
editor.resize(width, height);
}
height_input.onchange = () => {
height = parseInt((<HTMLInputElement>height_input).value);
editor.resize(width, height);
}
document.getElementById("saveMap").onclick = () => {
editor.am.exportMap(width, height, file);
};
document.getElementById("canvas").addEventListener("click", () => {
if (editor.selectedItems){
editor.setCurrentItem();
}
});
document.getElementById("loadMap").addEventListener("click", () => {
window.location.href = "/editor.html#"+prompt("What is the name of the level ? (The file should be located in your level folder)");
location.reload();
});
document.getElementById("canvas").addEventListener('contextmenu', event => event.preventDefault());
}*/
+11
View File
@@ -0,0 +1,11 @@
import {fullCity} from "./embedded/level/full_city";
export const embeddedUrl: any = {
fullCity: "embedded://level/fullCity"
}
export const embeddedContent: any = {
level: {
fullCity: fullCity
}
}
+272
View File
@@ -0,0 +1,272 @@
export const fullCity: any = {
"cars": [
{
"mx": 2,
"my": 5,
"line": 0,
"auto": true
},
{
"mx": 3,
"my": 4,
"line": 0,
"auto": true
},
{
"mx": 3,
"my": 4,
"line": 1,
"auto": true
},
{
"mx": 6,
"my": 3,
"line": 0,
"auto": true
},
{
"mx": 6,
"my": 3,
"line": 1,
"auto": true
},
{
"mx": 8,
"my": 2,
"line": 0,
"auto": true
},
{
"mx": 8,
"my": 2,
"line": 1,
"auto": true
},
{
"mx": 2,
"my": 7,
"line": 0,
"auto": true
}
],
"house": [
{
"x": 9,
"y": 409
},
{
"x": 146,
"y": 162
},
{
"x": 439,
"y": 39
}
],
"house2": [
{
"x": 425,
"y": 192
},
{
"x": 431,
"y": 354
}
],
"house3": [
{
"x": 426,
"y": 281
},
{
"x": 51,
"y": 157
},
{
"x": 553,
"y": 34
}
],
"bench": [
{
"x": 259,
"y": 206
},
{
"x": 8,
"y": 205
}
],
"tree": [
{
"x": 1,
"y": 303
},
{
"x": 54,
"y": 357
},
{
"x": 215,
"y": 325
},
{
"x": 230,
"y": 400
},
{
"x": 282,
"y": 368
},
{
"x": 182,
"y": 433
},
{
"x": 285,
"y": 309
},
{
"x": 576,
"y": 233
},
{
"x": 581,
"y": 300
},
{
"x": 519,
"y": 388
},
{
"x": 582,
"y": 391
},
{
"x": 256,
"y": 69
},
{
"x": 151,
"y": 43
},
{
"x": 77,
"y": 77
},
{
"x": 26,
"y": 24
},
{
"x": 189,
"y": 98
}
],
"map": [
[
0,
0,
0,
0,
0,
0,
"↕",
0,
0,
0,
0
],
[
0,
0,
0,
0,
0,
0,
"↠",
0,
0,
0,
0
],
[
0,
0,
0,
0,
0,
0,
"↦",
"↔",
"↔",
"↟",
"↔"
],
[
0,
0,
0,
0,
0,
0,
"↕",
0,
0,
0,
0
],
[
"↔",
"↔",
"↧",
"↔",
"↠",
"↔",
"↤",
0,
0,
0,
0
],
[
0,
0,
"↕",
0,
0,
0,
"↕",
0,
0,
0,
0
],
[
0,
0,
"↠",
0,
0,
0,
"↠",
0,
0,
0,
0
],
[
0,
0,
"↕",
0,
0,
0,
"↕",
0,
0,
0,
0
]
]
}
+1 -1
View File
@@ -13,7 +13,7 @@ export const Graphics = PIXI.Graphics;
export const Container = PIXI.Container;
// Textures files
export const JSON_TEXTURES = "textures/textures.json";
export const JSON_TEXTURES = "public/textures/textures.json";
// Level foler
export const LEVEL_FOLDER = "/level/";
+8 -1
View File
@@ -1,2 +1,9 @@
import {MetaCar} from "./metacar";
export default MetaCar;
import {embeddedUrl} from "./embedded";
const metacar = {
env: MetaCar,
level: embeddedUrl
}
export default metacar;
+17 -18
View File
@@ -8,9 +8,9 @@ import * as U from "./utils";
import {
LEVEL_FOLDER, ROADSIZE, Loader, JSON_TEXTURES
} from "./global";
import {AssetManger, RoadSprite, SimpleSprite, AssetInfo, } from "./asset_manager";
import {AssetManger, RoadSprite} from "./asset_manager";
import {Car} from "./car";
import {Car, CarSprite} from "./car";
export interface LevelInfo {
map: (string|number)[][];
@@ -24,26 +24,27 @@ export interface Roads {
export class Level {
private app: any = null; // The pixi app.
private name: string = null; // Name fo the level
public app: any = null; // The pixi app.
private info: LevelInfo = null; // Information of the loaded level
private envs: any[] = [] // Used to list all elements the car can crash with
private map: (string|number)[][] = null; // Use the roads positions of the level
private agent: any = null; // (@Car class) for the agent
public agent: any = null; // (@Car class) for the agent
// Object to store all the roads assets
// Each road is accesible as follow this.roads[[my, mx]]
// with my and mx the (x, y) position relative to the map (not pixel).
private roads: Roads;
private cars: any; // Array used to store all the cars assets
private roads: Roads = {};
private cars: Car[] = []; // Array used to store all the cars assets
private loop: any; // Loop method called for each render
private am: AssetManger; // The AssetManger is used to set most of the elements on the map
private canvasId: string; // Id of the target canvas
constructor(levelName: string, canvasId: string) {
constructor(levelContent: LevelInfo, canvasId: string) {
/*
@levelName: Name of the level to load (.json)
@levelContent: Content of the level
@canvasID: HTML canvas id
*/
this.name = levelName;
this.info = levelContent;
this.map = this.info.map;
this.canvasId = canvasId;
this.am = new AssetManger(this);
}
@@ -55,13 +56,7 @@ export class Level {
*/
this.loop = loop;
return new Promise((resolve, reject) => {
// Load the level
U.loadJSON(LEVEL_FOLDER + this.name, (response: string) => {
// Parse JSON string into object
this.info = JSON.parse(response);
this.map = this.info.map; // Store the map to let it accessible faster later on.
this.createLevel(this.info).then(() => resolve());
});
this.createLevel(this.info).then(() => resolve());
});
}
@@ -81,7 +76,7 @@ export class Level {
document.getElementById(this.canvasId).appendChild(this.app.view);
return new Promise((resolve, reject) => {
Loader.add(["textures/textures.json", "textures/textures.png"]).load(() => {
Loader.add(["public/textures/textures.json", "public/textures/textures.png"]).load(() => {
this.setup(info); // Set up the level (Add assets)
resolve();
});
@@ -193,6 +188,10 @@ export class Level {
getRoad(my: number, mx: number){
return this.roads[[my.toString(), mx.toString()].toString()];
}
getRoads(){
return this.roads;
}
findCarById(id: number){
/*
+36 -9
View File
@@ -2,26 +2,52 @@
Main class of the project
*/
import {Level} from "./level";
import {Level, LevelInfo} from "./level";
import * as U from "./utils";
export class MetaCar {
private isPlaying: boolean;
private agent: any;
private level: Level;
private canvasId: string;
private levelUrl: string;
constructor() {
constructor(canvasId: string, levelUrl: string) {
/**
* @canvasId: HTML canvas ID to used
* @level: URL or Local storage URL.
* localstorage://level-name
* embedded://
* http(s)://
*/
if (!canvasId || this.levelUrl){
console.error("You must specify the canvasId and the levelUrl");
}
this.isPlaying = false;
this.canvasId = canvasId;
this.levelUrl = levelUrl;
}
load(level: string, agent: any){
load(level: string, agent: any): Promise<void>{
/*
Load the environement
@level (String) Name of the json level to load
@agent (Agent class)
*/
this.isPlaying = false;
return new Promise((resolve, reject) => {
U.loadCustomURL(this.levelUrl, (content: LevelInfo) => {
this.level = new Level(content, this.canvasId);
this.level.load((delta: number) => this.loop(delta));
resolve();
});
});
/*
this.agent = agent;
this.level = new Level(level);
this.level = new Level(level, "canvas");
this.level.load((delta: number) => this.loop(delta));
document.getElementById("train").addEventListener("click", () => {
@@ -45,6 +71,7 @@ export class MetaCar {
document.getElementById("dumpFile").addEventListener("change", (e) => {
//readDump(e, (content) => this.agent.restore(this, content));
});
*/
}
render(){
@@ -59,7 +86,7 @@ export class MetaCar {
/*
Save the agent
*/
saveAs(content, file_name);
U.saveAs(content, file_name);
}
actionSpace(){
@@ -97,10 +124,11 @@ export class MetaCar {
This position
*/
this.level.agent.last_position = [];
let keys = Object.keys(this.level.roads);
let roads = this.level.getRoads();
let keys = Object.keys(roads);
keys.sort(function() {return Math.random()-0.5;});
for (let k in keys){
let road = this.level.roads[keys[k]];
let road = roads[keys[k]];
if (road.cars.length == 0){
road.setCarPosition(this.level.agent.core);
break;
@@ -115,7 +143,6 @@ export class MetaCar {
else {
this.level.step(delta);
}
document.getElementById("rewardDisplay").innerHTML = this.level.last_reward;
}
}
+8 -9
View File
@@ -8,6 +8,7 @@ import {Level} from "./level";
import {
MAP
} from "./global";
import { Editor } from "./editor";
export interface MotionOption{
readonly rotationStep?: number;
@@ -16,12 +17,12 @@ export interface MotionOption{
export class MotionEngine {
protected level: Level;
protected level: Level|Editor;
protected car: any;
protected lidar: any;
protected state: any;
constructor(level: Level) {
constructor(level: Level|Editor) {
this.level = level;
}
@@ -53,7 +54,7 @@ export class MotionEngine {
if (envs[i] != this.car && envs[i].obstacle && this.boxesIntersect(envs[i], this.car)){
agent_col.push(envs[i]);
}
else if (envs[i].map_id == MAP.ROAD && this.boxesIntersect(envs[i], this.car)){
else if (envs[i].mapId == MAP.ROAD && this.boxesIntersect(envs[i], this.car)){
on_road = true;
}
if (this.boxesIntersect(envs[i], this.lidar)){
@@ -70,16 +71,14 @@ export class MotionEngine {
Set up the current state of the agent
@lidar_collisions List with all possible lidar collisions
*/
let state = [];
let f = 0.1;
let pt_id = 0;
for (var i = 0; i < this.lidar.children.length; i++) {
this.lidar.children[i].alpha = 0.1;
if (this.lidar.children[i].pt){ // If this is a lidar point
let pt_y = Math.round(pt_id/this.lidar.pts);
let pt_x = Math.round(pt_id%this.lidar.pts);
let pt_y = Math.floor(pt_id/this.lidar.pts);
let pt_x = Math.floor(pt_id%this.lidar.pts);
this.state[pt_y][pt_x] = MAP.DEFAULT;
for (var a = 0; a < lidar_collisions.length; a++) {
@@ -89,11 +88,11 @@ export class MotionEngine {
// If this is an obstacle
this.lidar.children[i].alpha = 1.;
}
if (touch && (lidar_collisions[a].map_id > this.state[pt_y][pt_x] || (lidar_collisions[a].map_id == MAP.ROAD && this.state[pt_y][pt_x]==MAP.DEFAULT))){
if (touch && (lidar_collisions[a].mapId > this.state[pt_y][pt_x] || (lidar_collisions[a].mapId == MAP.ROAD && this.state[pt_y][pt_x]==MAP.DEFAULT))){
// Add this interaction to the state
// If this interaction is more important than the one befor
// we kept the more important one
this.state[pt_y][pt_x] = lidar_collisions[a].map_id;
this.state[pt_y][pt_x] = lidar_collisions[a].mapId;
}
}
}
+31 -2
View File
@@ -1,3 +1,5 @@
import {embeddedContent} from "./embedded";
/*
File with some usefull methods
*/
@@ -19,6 +21,33 @@ export function loadJSON(url: string, callback: any) {
xobj.send(null);
}
export function loadEmbeddedURL(url: string, callback: any):void {
let split = url.split("//");
split = split[1].split("/");
callback(embeddedContent[split[0]][split[1]]);
}
export function loadLocalStorageURL(url: string, callback: any):void{
}
export function loadCustomURL(url: string, callback: any):void {
/**
* @url: CustomURL to load
* localstorage://level-name
* http(s)://
* embedded:://
@callback function onece the content is loaded
*/
const customCall: any = {
"embedded:": loadEmbeddedURL,
"http://": loadJSON,
"https://": loadJSON,
"localstorage://": loadLocalStorageURL
}
const split = url.split("//");
customCall[split[0]](url, callback);
}
export function mod(n: number, m:number) {
return ((n % m) + m) % m;
}
@@ -33,7 +62,7 @@ export function argMax(array: number[]) {
return array.map((x, i) => [x, i]).reduce((r, a) => (a[0] > r[0] ? a : r))[1];
}
export function mean(array: number){
export function mean(array: number[]){
/**
* Return the mean of the array
*/
@@ -44,7 +73,7 @@ export function mean(array: number){
return avg;
}
function saveAs(content: string, name: string) {
export function saveAs(content: string, name: string) {
var a = document.createElement("a");
//a.style = "display: none";
document.body.appendChild(a);