added a working physics system
This commit is contained in:
parent
4b8ebf360d
commit
ea33e71619
|
@ -55,6 +55,9 @@ export interface Physical {
|
|||
/** The shape of the collider for this physics object. */
|
||||
collisionShape: Shape;
|
||||
|
||||
/** The offset of the collision shape from the center of the node */
|
||||
colliderOffset: Vec2;
|
||||
|
||||
/** Represents whether this object can move or not. */
|
||||
isStatic: boolean;
|
||||
|
||||
|
@ -84,6 +87,8 @@ export interface Physical {
|
|||
|
||||
isPlayer: boolean;
|
||||
|
||||
isColliding: boolean;
|
||||
|
||||
/*---------- FUNCTIONS ----------*/
|
||||
|
||||
/**
|
||||
|
@ -104,7 +109,7 @@ export interface Physical {
|
|||
* @param isCollidable Whether this object will be able to collide with other objects
|
||||
* @param isStatic Whether this object will be static or not
|
||||
*/
|
||||
addPhysics: (collisionShape?: Shape, isCollidable?: boolean, isStatic?: boolean) => void;
|
||||
addPhysics: (collisionShape?: Shape, colliderOffset?: Vec2, isCollidable?: boolean, isStatic?: boolean) => void;
|
||||
|
||||
/**
|
||||
* Adds a trigger to this object for a specific group
|
||||
|
@ -118,6 +123,11 @@ export interface Physical {
|
|||
* @param layer The name of the layer
|
||||
*/
|
||||
setPhysicsLayer: (layer: String) => void;
|
||||
|
||||
/**
|
||||
* If used before "move()", it will tell you the velocity of the node after its last movement
|
||||
*/
|
||||
getLastVelocity(): Vec2;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,6 +2,7 @@ import Shape from "./Shape";
|
|||
import Vec2 from "../Vec2";
|
||||
import MathUtils from "../../Utils/MathUtils";
|
||||
import Circle from "./Circle";
|
||||
import Debug from "../../Debug/Debug";
|
||||
|
||||
export default class AABB extends Shape {
|
||||
|
||||
|
@ -109,25 +110,20 @@ export default class AABB extends Shape {
|
|||
* @param paddingX Pads the AABB in the x axis
|
||||
* @param paddingY Pads the AABB in the y axis
|
||||
*/
|
||||
intersectSegment(point: Vec2, direction: Vec2, distance?: number, paddingX?: number, paddingY?: number): Hit {
|
||||
// Scale by the distance if it has been provided
|
||||
if(distance){
|
||||
direction = direction.scaled(distance);
|
||||
}
|
||||
intersectSegment(point: Vec2, delta: Vec2, padding?: Vec2): Hit {
|
||||
let paddingX = padding ? padding.x : 0;
|
||||
let paddingY = padding ? padding.y : 0;
|
||||
|
||||
let _paddingX = paddingX ? paddingX : 0;
|
||||
let _paddingY = paddingY ? paddingY : 0;
|
||||
|
||||
let scaleX = 1/direction.x;
|
||||
let scaleY = 1/direction.y;
|
||||
let scaleX = 1/delta.x;
|
||||
let scaleY = 1/delta.y;
|
||||
|
||||
let signX = MathUtils.sign(scaleX);
|
||||
let signY = MathUtils.sign(scaleY);
|
||||
|
||||
let tnearx = scaleX*(this.x - signX*(this.hw + _paddingX) - point.x);
|
||||
let tneary = scaleX*(this.y - signY*(this.hh + _paddingY) - point.y);
|
||||
let tfarx = scaleY*(this.x + signX*(this.hw + _paddingX) - point.x);
|
||||
let tfary = scaleY*(this.y + signY*(this.hh + _paddingY) - point.y);
|
||||
let tnearx = scaleX*(this.x - signX*(this.hw + paddingX) - point.x);
|
||||
let tneary = scaleY*(this.y - signY*(this.hh + paddingY) - point.y);
|
||||
let tfarx = scaleX*(this.x + signX*(this.hw + paddingX) - point.x);
|
||||
let tfary = scaleY*(this.y + signY*(this.hh + paddingY) - point.y);
|
||||
|
||||
if(tnearx > tfary || tneary > tfarx){
|
||||
// We aren't colliding - we clear one axis before intersecting another
|
||||
|
@ -135,15 +131,29 @@ export default class AABB extends Shape {
|
|||
}
|
||||
|
||||
let tnear = Math.max(tnearx, tneary);
|
||||
|
||||
// Double check for NaNs
|
||||
if(tnearx !== tnearx){
|
||||
tnear = tneary;
|
||||
} else if (tneary !== tneary){
|
||||
tnear = tnearx;
|
||||
}
|
||||
|
||||
let tfar = Math.min(tfarx, tfary);
|
||||
|
||||
if(tnear === -Infinity){
|
||||
return null;
|
||||
}
|
||||
|
||||
if(tnear >= 1 || tfar <= 0){
|
||||
return null;
|
||||
}
|
||||
|
||||
// We are colliding
|
||||
let hit = new Hit();
|
||||
hit.t = MathUtils.clamp01(tnear);
|
||||
hit.time = MathUtils.clamp01(tnear);
|
||||
hit.nearTimes.x = tnearx;
|
||||
hit.nearTimes.y = tneary;
|
||||
|
||||
if(tnearx > tneary){
|
||||
// We hit on the left or right size
|
||||
|
@ -154,10 +164,10 @@ export default class AABB extends Shape {
|
|||
hit.normal.y = -signY;
|
||||
}
|
||||
|
||||
hit.delta.x = (1.0 - hit.t) * -direction.x;
|
||||
hit.delta.y = (1.0 - hit.t) * -direction.y;
|
||||
hit.pos.x = point.x + direction.x * hit.t;
|
||||
hit.pos.y = point.y + direction.y * hit.t;
|
||||
hit.delta.x = (1.0 - hit.time) * -delta.x;
|
||||
hit.delta.y = (1.0 - hit.time) * -delta.y;
|
||||
hit.pos.x = point.x + delta.x * hit.time;
|
||||
hit.pos.y = point.y + delta.y * hit.time;
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
@ -241,7 +251,8 @@ export default class AABB extends Shape {
|
|||
}
|
||||
|
||||
export class Hit {
|
||||
t: number;
|
||||
time: number;
|
||||
nearTimes: Vec2 = Vec2.ZERO;
|
||||
pos: Vec2 = Vec2.ZERO;
|
||||
delta: Vec2 = Vec2.ZERO;
|
||||
normal: Vec2 = Vec2.ZERO;
|
||||
|
|
|
@ -94,6 +94,7 @@ export default abstract class CanvasNode extends GameNode implements Region {
|
|||
|
||||
debugRender(): void {
|
||||
super.debugRender();
|
||||
Debug.drawBox(this.relativePosition, this.sizeWithZoom, false, Color.GREEN);
|
||||
let color = this.isColliding ? Color.RED : Color.GREEN;
|
||||
Debug.drawBox(this.relativePosition, this.sizeWithZoom, false, color);
|
||||
}
|
||||
}
|
|
@ -31,6 +31,7 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
|
|||
onCeiling: boolean;
|
||||
active: boolean;
|
||||
collisionShape: Shape;
|
||||
colliderOffset: Vec2;
|
||||
isStatic: boolean;
|
||||
isCollidable: boolean;
|
||||
isTrigger: boolean;
|
||||
|
@ -41,6 +42,7 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
|
|||
collidedWithTilemap: boolean;
|
||||
physicsLayer: number;
|
||||
isPlayer: boolean;
|
||||
isColliding: boolean = false;
|
||||
|
||||
/*---------- ACTOR ----------*/
|
||||
_ai: AI;
|
||||
|
@ -128,7 +130,7 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
|
|||
* @param isCollidable Whether this is collidable or not. True by default.
|
||||
* @param isStatic Whether this is static or not. False by default
|
||||
*/
|
||||
addPhysics = (collisionShape?: Shape, isCollidable: boolean = true, isStatic: boolean = false): void => {
|
||||
addPhysics = (collisionShape?: Shape, colliderOffset?: Vec2, isCollidable: boolean = true, isStatic: boolean = false): void => {
|
||||
this.hasPhysics = true;
|
||||
this.moving = false;
|
||||
this.onGround = false;
|
||||
|
@ -154,6 +156,12 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
|
|||
throw "No collision shape specified for physics object."
|
||||
}
|
||||
|
||||
if(colliderOffset){
|
||||
this.colliderOffset = colliderOffset;
|
||||
} else {
|
||||
this.colliderOffset = Vec2.ZERO;
|
||||
}
|
||||
|
||||
this.sweptRect = this.collisionShape.getBoundingRect();
|
||||
this.scene.getPhysicsManager().registerObject(this);
|
||||
}
|
||||
|
@ -171,6 +179,10 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
|
|||
this.scene.getPhysicsManager().setLayer(this, layer);
|
||||
}
|
||||
|
||||
getLastVelocity(): Vec2 {
|
||||
return this._velocity;
|
||||
}
|
||||
|
||||
/*---------- ACTOR ----------*/
|
||||
get ai(): AI {
|
||||
return this._ai;
|
||||
|
@ -251,7 +263,7 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
|
|||
*/
|
||||
protected positionChanged(): void {
|
||||
if(this.hasPhysics){
|
||||
this.collisionShape.center = this.position;
|
||||
this.collisionShape.center = this.position.clone().add(this.colliderOffset);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -260,11 +272,17 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
|
|||
}
|
||||
|
||||
debugRender(): void {
|
||||
Debug.drawPoint(this.relativePosition, Color.GREEN);
|
||||
let color = this.isColliding ? Color.RED : Color.GREEN;
|
||||
Debug.drawPoint(this.relativePosition, color);
|
||||
|
||||
// If velocity is not zero, draw a vector for it
|
||||
if(this._velocity && !this._velocity.isZero()){
|
||||
Debug.drawRay(this.relativePosition, this._velocity.clone().scaleTo(20).add(this.relativePosition), Color.GREEN);
|
||||
Debug.drawRay(this.relativePosition, this._velocity.clone().scaleTo(20).add(this.relativePosition), color);
|
||||
}
|
||||
|
||||
// If this has a collider, draw it
|
||||
if(this.isCollidable && this.collisionShape){
|
||||
Debug.drawBox(this.collisionShape.center, this.collisionShape.halfSize, false, Color.RED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ export default class OrthogonalTilemap extends Tilemap {
|
|||
|
||||
// The size of the tilemap on the canvas
|
||||
this.size.set(this.numCols * this.tileSize.x, this.numRows * this.tileSize.y);
|
||||
this.position.copy(this.size);
|
||||
this.position.copy(this.size.scaled(0.5));
|
||||
this.data = layer.data;
|
||||
this.visible = layer.visible;
|
||||
|
||||
|
|
|
@ -91,6 +91,12 @@ export default class BasicPhysicsManager extends PhysicsManager {
|
|||
resolveCollision(node1: Physical, node2: Physical, firstContact: Vec2, lastContact: Vec2, collidingX: boolean, collidingY: boolean): void {
|
||||
// Handle collision
|
||||
if( (firstContact.x < 1 || collidingX) && (firstContact.y < 1 || collidingY)){
|
||||
if(node1.isPlayer){
|
||||
node1.isColliding = true;
|
||||
} else if(node2.isPlayer){
|
||||
node2.isColliding = true;
|
||||
}
|
||||
|
||||
// We are colliding. Check for any triggers
|
||||
let group1 = node1.group;
|
||||
let group2 = node2.group;
|
||||
|
@ -109,7 +115,7 @@ export default class BasicPhysicsManager extends PhysicsManager {
|
|||
}
|
||||
|
||||
if(collidingX && collidingY){
|
||||
// If we're already intersecting, freak out I guess? Probably should handle this in some way for if nodes get spawned inside of tiles
|
||||
// If we're already intersecting, resolve the current collision
|
||||
} else if(node1.isCollidable && node2.isCollidable) {
|
||||
// We aren't already colliding, and both nodes can collide, so this is a new collision.
|
||||
|
||||
|
@ -264,6 +270,11 @@ export default class BasicPhysicsManager extends PhysicsManager {
|
|||
node.onCeiling = false;
|
||||
node.onWall = false;
|
||||
node.collidedWithTilemap = false;
|
||||
node.isColliding = false;
|
||||
|
||||
if(node.isPlayer){
|
||||
Debug.log("pvel", "Player Velocity:", node._velocity.toString());
|
||||
}
|
||||
|
||||
// Update the swept shapes of each node
|
||||
if(node.moving){
|
||||
|
|
229
src/Physics/TestPhysicsManager.ts
Normal file
229
src/Physics/TestPhysicsManager.ts
Normal file
|
@ -0,0 +1,229 @@
|
|||
import GameNode from "../Nodes/GameNode";
|
||||
import { Physical, Updateable } from "../DataTypes/Interfaces/Descriptors";
|
||||
import Tilemap from "../Nodes/Tilemap";
|
||||
import PhysicsManager from "./PhysicsManager";
|
||||
import Vec2 from "../DataTypes/Vec2";
|
||||
import Debug from "../Debug/Debug";
|
||||
import Color from "../Utils/Color";
|
||||
import AABB from "../DataTypes/Shapes/AABB";
|
||||
import OrthogonalTilemap from "../Nodes/Tilemaps/OrthogonalTilemap";
|
||||
|
||||
export default class TestPhysicsManager extends PhysicsManager {
|
||||
|
||||
/** The array of static nodes */
|
||||
protected staticNodes: Array<Physical>;
|
||||
|
||||
/** The array of dynamic nodes */
|
||||
protected dynamicNodes: Array<Physical>;
|
||||
|
||||
/** The array of tilemaps */
|
||||
protected tilemaps: Array<Tilemap>;
|
||||
|
||||
constructor(){
|
||||
super();
|
||||
this.staticNodes = new Array();
|
||||
this.dynamicNodes = new Array();
|
||||
this.tilemaps = new Array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new physics object to be updated with the physics system
|
||||
* @param node The node to be added to the physics system
|
||||
*/
|
||||
registerObject(node: GameNode): void {
|
||||
if(node.isStatic){
|
||||
// Static and not collidable
|
||||
this.staticNodes.push(node);
|
||||
} else {
|
||||
// Dynamic and not collidable
|
||||
this.dynamicNodes.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a tilemap with this physics manager
|
||||
* @param tilemap
|
||||
*/
|
||||
registerTilemap(tilemap: Tilemap): void {
|
||||
this.tilemaps.push(tilemap);
|
||||
}
|
||||
|
||||
setLayer(node: GameNode, layer: string): void {
|
||||
node.physicsLayer = this.layerMap.get(layer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the physics
|
||||
* @param deltaT
|
||||
*/
|
||||
update(deltaT: number): void {
|
||||
/* ALGORITHM:
|
||||
In an effort to keep things simple and working effectively, each dynamic node will resolve its
|
||||
collisions considering the rest of the world as static.
|
||||
|
||||
Collision detecting will happen first. This can be considered a broad phase, but it is not especially
|
||||
efficient, as it does not need to be for this game engine. Every dynamic node is checked against every
|
||||
other node for collision area. If collision area is non-zero (meaning the current node sweeps into another),
|
||||
it is added to a list of hits.
|
||||
|
||||
INITIALIZATION:
|
||||
- Physics constants are reset
|
||||
- Swept shapes are recalculated. If a node isn't moving, it is skipped.
|
||||
|
||||
COLLISION DETECTION:
|
||||
- For a node, collision area will be calculated using the swept AABB of the node against every other AABB in a static state
|
||||
- These collisions will be sorted by area in descending order
|
||||
|
||||
COLLISION RESOLUTION:
|
||||
- For each hit, time of collision is calculated using a swept line through the AABB of the static node expanded
|
||||
with minkowski sums (discretely, but the concept is there)
|
||||
- The collision is resolved based on the near time of the collision (from method of separated axes)
|
||||
- X is resolved by near x, Y by near y.
|
||||
- There is some fudging to allow for sliding along walls of separate colliders. Sorting by area also helps with this.
|
||||
- Corner to corner collisions are resolve to favor x-movement. This is in consideration of platformers, to give
|
||||
the player some help with jumps
|
||||
|
||||
Pros:
|
||||
- Everything happens with a consistent time. There is a distinct before and after for each resolution.
|
||||
- No back-tracking needs to be done. Once we resolve a node, it is definitively resolved.
|
||||
|
||||
Cons:
|
||||
- Nodes that are processed early have movement priority over other nodes. This can lead to some undesirable interactions.
|
||||
*/
|
||||
for(let node of this.dynamicNodes){
|
||||
/*---------- INITIALIZATION PHASE ----------*/
|
||||
// Clear frame dependent boolean values for each node
|
||||
node.onGround = false;
|
||||
node.onCeiling = false;
|
||||
node.onWall = false;
|
||||
node.collidedWithTilemap = false;
|
||||
|
||||
// Update the swept shapes of each node
|
||||
if(node.moving){
|
||||
// If moving, reflect that in the swept shape
|
||||
node.sweptRect.sweep(node._velocity, node.collisionShape.center, node.collisionShape.halfSize);
|
||||
} else {
|
||||
// If our node isn't moving, don't bother to check it (other nodes will detect if they run into it)
|
||||
node._velocity.zero();
|
||||
continue;
|
||||
}
|
||||
|
||||
/*---------- DETECTION PHASE ----------*/
|
||||
// Gather a set of overlaps
|
||||
let overlaps = new Array<AreaCollision>();
|
||||
|
||||
// First, check this node against every static node (order doesn't actually matter here, since we sort anyways)
|
||||
for(let other of this.staticNodes){
|
||||
let collider = other.collisionShape.getBoundingRect();
|
||||
let area = node.sweptRect.overlapArea(collider);
|
||||
if(area > 0){
|
||||
// We had a collision
|
||||
overlaps.push(new AreaCollision(area, collider));
|
||||
}
|
||||
}
|
||||
|
||||
// Then, check it against every dynamic node
|
||||
for(let other of this.dynamicNodes){
|
||||
let collider = other.collisionShape.getBoundingRect();
|
||||
let area = node.sweptRect.overlapArea(collider);
|
||||
if(area > 0){
|
||||
// We had a collision
|
||||
overlaps.push(new AreaCollision(area, collider));
|
||||
}
|
||||
}
|
||||
|
||||
// Lastly, gather a set of AABBs from the tilemap.
|
||||
// This step involves the most extra work, so it is abstracted into a method
|
||||
for(let tilemap of this.tilemaps){
|
||||
if(tilemap instanceof OrthogonalTilemap){
|
||||
this.collideWithOrthogonalTilemap(node, tilemap, overlaps);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the overlaps by area
|
||||
overlaps = overlaps.sort((a, b) => b.area - a.area);
|
||||
|
||||
|
||||
/*---------- RESOLUTION PHASE ----------*/
|
||||
// For every overlap, determine if we need to collide with it and when
|
||||
for(let other of overlaps){
|
||||
// Do a swept line test on the static AABB with this AABB size as padding (this is basically using a minkowski sum!)
|
||||
// Start the sweep at the position of this node with a delta of _velocity
|
||||
const point = node.collisionShape.center;
|
||||
const delta = node._velocity;
|
||||
const padding = node.collisionShape.halfSize;
|
||||
const otherAABB = other.collider;
|
||||
|
||||
|
||||
const hit = otherAABB.intersectSegment(node.collisionShape.center, node._velocity, node.collisionShape.halfSize);
|
||||
|
||||
if(hit !== null){
|
||||
// We got a hit, resolve with the time inside of the hit
|
||||
let tnearx = hit.nearTimes.x;
|
||||
let tneary = hit.nearTimes.y;
|
||||
|
||||
// Allow edge clipping (edge overlaps don't count, only area overlaps)
|
||||
// Importantly don't allow both cases to be true. Then we clip through corners. Favor x to help players land jumps
|
||||
if(tnearx < 1.0 && (point.y === otherAABB.top - padding.y || point.y === otherAABB.bottom + padding.y) && delta.x !== 0) {
|
||||
tnearx = 1.0;
|
||||
} else if(tneary < 1.0 && (point.x === otherAABB.left - padding.x || point.x === otherAABB.right + padding.x) && delta.y !== 0) {
|
||||
tneary = 1.0;
|
||||
}
|
||||
|
||||
|
||||
if(hit.nearTimes.x >= 0 && hit.nearTimes.x < 1){
|
||||
node._velocity.x = node._velocity.x * tnearx;
|
||||
}
|
||||
|
||||
if(hit.nearTimes.y >= 0 && hit.nearTimes.y < 1){
|
||||
node._velocity.y = node._velocity.y * tneary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve the collision with the node, and move it
|
||||
node.finishMove();
|
||||
}
|
||||
}
|
||||
|
||||
collideWithOrthogonalTilemap(node: Physical, tilemap: OrthogonalTilemap, overlaps: Array<AreaCollision>): void {
|
||||
// Get the min and max x and y coordinates of the moving node
|
||||
let min = new Vec2(node.sweptRect.left, node.sweptRect.top);
|
||||
let max = new Vec2(node.sweptRect.right, node.sweptRect.bottom);
|
||||
|
||||
// Convert the min/max x/y to the min and max row/col in the tilemap array
|
||||
let minIndex = tilemap.getColRowAt(min);
|
||||
let maxIndex = tilemap.getColRowAt(max);
|
||||
|
||||
let tileSize = tilemap.getTileSize();
|
||||
|
||||
// Loop over all possible tiles (which isn't many in the scope of the velocity per frame)
|
||||
for(let col = minIndex.x; col <= maxIndex.x; col++){
|
||||
for(let row = minIndex.y; row <= maxIndex.y; row++){
|
||||
if(tilemap.isTileCollidable(col, row)){
|
||||
// Get the position of this tile
|
||||
let tilePos = new Vec2(col * tileSize.x + tileSize.x/2, row * tileSize.y + tileSize.y/2);
|
||||
|
||||
// Create a new collider for this tile
|
||||
let collider = new AABB(tilePos, tileSize.scaled(1/2));
|
||||
|
||||
// Calculate collision area between the node and the tile
|
||||
let area = node.sweptRect.overlapArea(collider);
|
||||
if(area > 0){
|
||||
// We had a collision
|
||||
overlaps.push(new AreaCollision(area, collider));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AreaCollision {
|
||||
area: number;
|
||||
collider: AABB;
|
||||
constructor(area: number, collider: AABB){
|
||||
this.area = area;
|
||||
this.collider = collider;
|
||||
}
|
||||
}
|
|
@ -187,7 +187,7 @@ export default class TilemapFactory {
|
|||
|
||||
// Now we have sprite. Associate it with our physics object if there is one
|
||||
if(hasPhysics){
|
||||
sprite.addPhysics(sprite.boundary.clone(), isCollidable, isStatic);
|
||||
sprite.addPhysics(sprite.boundary.clone(), Vec2.ZERO, isCollidable, isStatic);
|
||||
sprite.group = group;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,55 +1,69 @@
|
|||
import AABB from "../../DataTypes/Shapes/AABB";
|
||||
import { TiledTilemapData } from "../../DataTypes/Tilesets/TiledData";
|
||||
import Vec2 from "../../DataTypes/Vec2";
|
||||
import Debug from "../../Debug/Debug";
|
||||
import InputHandler from "../../Input/InputHandler";
|
||||
import InputReceiver from "../../Input/InputReceiver";
|
||||
import CanvasNode from "../../Nodes/CanvasNode";
|
||||
import { GraphicType } from "../../Nodes/Graphics/GraphicTypes";
|
||||
import BasicPhysicsManager from "../../Physics/BasicPhysicsManager";
|
||||
import TestPhysicsManager from "../../Physics/TestPhysicsManager";
|
||||
import Scene from "../../Scene/Scene";
|
||||
import Color from "../../Utils/Color";
|
||||
|
||||
export default class TestScene extends Scene {
|
||||
|
||||
loadScene(){
|
||||
this.load.tilemap("test", "assets/tilemaps/PhysicsTest.json");
|
||||
}
|
||||
|
||||
startScene(){
|
||||
// Opt into a custom physics manager
|
||||
this.physicsManager = new BasicPhysicsManager(this.sceneOptions.physics);
|
||||
this.physicsManager = new TestPhysicsManager();
|
||||
|
||||
let tilemap = <CanvasNode>this.add.tilemap("test")[0].getItems()[0];
|
||||
|
||||
let layer = this.getLayer("MovingObject");
|
||||
layer.getItems().forEach(item => {
|
||||
let timer = 0;
|
||||
let dir = new Vec2(-1, 0);
|
||||
item.update = (deltaT: number) => {
|
||||
if(timer > 2){
|
||||
timer = 0;
|
||||
dir.scale(-1);
|
||||
}
|
||||
|
||||
item.move(dir.scaled(100*deltaT));
|
||||
|
||||
timer += deltaT;
|
||||
}
|
||||
})
|
||||
|
||||
this.addLayer("main");
|
||||
|
||||
let player = this.add.graphic(GraphicType.RECT, "main", {position: new Vec2(100, 100), size: new Vec2(100, 100)});
|
||||
player.addPhysics();
|
||||
let player = this.add.graphic(GraphicType.RECT, "main", {position: new Vec2(50, 100), size: new Vec2(45, 45)});
|
||||
player.color = Color.ORANGE;
|
||||
player.addPhysics(new AABB(new Vec2(0, 0), new Vec2(15, 15)), new Vec2(0, 7.5));
|
||||
|
||||
player.update = (deltaT: number) => {
|
||||
const input = InputReceiver.getInstance()
|
||||
|
||||
let xDir = (input.isPressed("a") ? -1 : 0) + (input.isPressed("d") ? 1 : 0);
|
||||
let yDir = (input.isPressed("w") ? -1 : 0) + (input.isPressed("s") ? 1 : 0);
|
||||
let yDir = input.isJustPressed("space") ? -1 : 0;
|
||||
|
||||
let dir = new Vec2(xDir, yDir);
|
||||
dir.normalize();
|
||||
let dir = new Vec2(xDir * 300 * deltaT, yDir*1000 * deltaT);
|
||||
|
||||
// Gravity
|
||||
if(dir.y === 0){
|
||||
dir.y = player.getLastVelocity().y + 50 * deltaT;
|
||||
}
|
||||
|
||||
Debug.log("pvel", player.getLastVelocity());
|
||||
|
||||
if(!dir.isZero()){
|
||||
player.move(dir.scale(deltaT * 300));
|
||||
player.move(dir);
|
||||
}
|
||||
}
|
||||
|
||||
let block = this.add.graphic(GraphicType.RECT, "main", {position: new Vec2(300, 500), size: new Vec2(100, 100)});
|
||||
block.color = Color.CYAN;
|
||||
block.addPhysics(block.boundary, true, true);
|
||||
|
||||
let movingBlock = this.add.graphic(GraphicType.RECT, "main", {position: new Vec2(500, 200), size: new Vec2(100, 100)});
|
||||
movingBlock.color = Color.CYAN;
|
||||
movingBlock.addPhysics();
|
||||
|
||||
let timer = 0;
|
||||
let dir = new Vec2(1, 0);
|
||||
movingBlock.update = (deltaT: number) => {
|
||||
if(timer > 0.5){
|
||||
timer = 0;
|
||||
dir.scale(-1);
|
||||
}
|
||||
|
||||
movingBlock.move(dir.scaled(200*deltaT));
|
||||
|
||||
timer += deltaT;
|
||||
}
|
||||
player.isPlayer = true;
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ function main(){
|
|||
game.start();
|
||||
|
||||
let sm = game.getSceneManager();
|
||||
sm.addScene(MainMenu, {});
|
||||
sm.addScene(TestScene, {});
|
||||
}
|
||||
|
||||
CanvasRenderingContext2D.prototype.roundedRect = function(x: number, y: number, w: number, h: number, r: number): void {
|
||||
|
|
Loading…
Reference in New Issue
Block a user