In de game kan je Realistic Physics
of Arcade Physics
physics aanzetten.
- "Arcade" style physics which is good for basic collision detection for non-rotated rectangular areas. Example: platformers, tile based games, top down, etc
- "Realistic" style physics which is good for rigid body games where realistic collisions are desired. Example: block stacking, angry bird's style games, etc
Per object kan je het type physics collision aanpassen.
CollisionType.Active
(volledige physics simulatie)CollisionType.Passive
(wel events, geen physics)CollisionType.Fixed
(collision events, kan niet bewegen)
In de main game zet je physics aan en bepaal je de world gravity. Voor een space game of top-down game zet je de gravity op 0. Je kan ook per object de body.useGravity
op true of false zetten.
GAME
const options = {
width: 800, height: 600,
backgroundColor: Color.White,
physics: {
solver: SolverStrategy.Realistic,
gravity: new Vector(0, 800),
}
}
export class Game extends Engine {
constructor() {
super(options)
this.start(ResourceLoader).then(() => this.startGame())
}
}
PLAYER - BOX COLLIDER
export class Player extends Actor {
constructor(x, y) {
super({ width: 50, height: 10 })
this.body.collisionType = CollisionType.Active
}
}
PLATFORM - STATIC BOX COLLIDER
export class Platform extends Actor {
constructor(x, y) {
super({ width: 500, height: 100 })
this.body.collisionType = CollisionType.Fixed
}
}
De hitbox hoeft niet hetzelfde te zijn als de width,height
van de sprite. In dit voorbeeld maken we een custom hitbox.
export class Player extends Actor {
onInitialize(engine) {
const customHitbox = Shape.Box(100, 100, new Vector(10,10)) // width, height, offset
this.collider.set(customHitbox)
}
}
Je kan een physics body de volgende properties meegeven:
this.body.mass
this.body.inertia
this.body.bounciness
(alleen bij useRealisticPhysics)this.body.friction
(alleen bij useRealisticPhysics)
De physics engine regelt de velocity
van je objecten zoals de speler. Effecten zoals stuiteren zal je niet zien als je handmatig de velocity
van een object gaat aanpassen. Je kan impulse
gebruiken om een richting aan de bestaande velocity
te geven. Dit wordt beïnvloed door mass
. Een zwaarder object zal moeizamer op snelheid komen. Dit werkt goed voor besturing van (ruimte) schepen of auto's. Als je handmatig de velocity
zet, dan voelt de besturing hetzelfde als in een non-physics game.
VOORBEELD
class SpaceShip extends Actor {
constructor(x, y) {
super({ width: 20, height: 60 })
this.body.collisionType = CollisionType.Active
this.body.mass = 7
}
onPreUpdate(engine, delta) {
if (engine.input.keyboard.isHeld(Keys.D)) {
this.body.applyLinearImpulse(new Vector(15 * delta, 0))
}
if (engine.input.keyboard.isHeld(Keys.A)) {
this.body.applyLinearImpulse(new Vector(-15 * delta, 0))
}
if (this.grounded) {
if (engine.input.keyboard.wasPressed(Keys.Space)) {
this.body.applyLinearImpulse(new Vector(0, -250 * delta))
this.grounded = false // grounded weer op true zetten na collision met ground
// alternatief voor springen met velocity
// this.vel = new Vector(this.vel.x, this.vel.y - 400)
}
}
}
}
Als je Realistic Physics gebruikt, dan wil je waarschijnlijk niet dat je karakter kan ronddraaien. Dit kan je uitzetten:
this.body.limitDegreeOfFreedom.push(DegreeOfFreedom.Rotation)
export class Ball extends Actor {
constructor(){
super({ radius: 50 })
this.graphics.use(Resources.Ball.toSprite())
this.body.collisionType = CollisionType.Active
this.body.mass = 6
this.body.bounciness = 0.7
this.pos = new Vector(350, -50)
}
}
In dit voorbeeld maken we een triangle collider in de onInitialize()
.
export class Triangle extends Actor {
onInitialize(engine) {
const triangle = new PolygonCollider({
points: [new Vector(-50, 0), new Vector(0, -80), new Vector(50, 0)]
});
this.body.collisionType = CollisionType.Fixed
this.collider.set(triangle)
this.pos = new Vector(120, 480)
}
}
Soms wil je alleen een rand hebben waar de speler niet langs mag. Dit kan je doen met een edge collider.
In dit voorbeeld loopt de edge van 0,0
naar 200,200
.
export class Border extends Actor {
constructor() {
super()
let edge = new EdgeCollider({
begin: new Vector(0, 0),
end: new Vector(600, 20),
})
this.pos = new Vector(100, 500)
this.body.collisionType = CollisionType.Fixed
this.collider.set(edge)
}
}
Je kan meerdere collision shapes (circles, edges en boxes) samenvoegen tot 1 collider met een complexe vorm. Hieronder een voorbeeld van een capsule (twee circles en een box) en een coastline (onregelmatige lijnen).
capsule
import { Shape, Actor, Vector, CollisionType, CompositeCollider } from "excalibur"
export class Player extends Actor {
onInitialize(engine) {
let capsule = new CompositeCollider([
Shape.Circle(10, new Vector(0, -20)),
Shape.Box(20, 40),
Shape.Circle(10, new Vector(0, 20)),
])
this.body.collisionType = CollisionType.Active
this.collider.set(capsule)
this.pos = new Vector(400, 100)
}
}
coastline
export class CoastLine extends Actor {
onInitialize(engine) {
let landscape = new CompositeCollider([
Shape.Edge(new Vector(0, 0), new Vector(120, 30)),
Shape.Edge(new Vector(120, 30), new Vector(240, 50)),
Shape.Edge(new Vector(240, 50), new Vector(320, 10)),
Shape.Edge(new Vector(320, 10), new Vector(430, 35))
])
this.body.collisionType = CollisionType.Fixed
this.collider.set(landscape)
this.pos = new Vector(400, 350)
}
}