Skip to content

Commit

Permalink
complete platformEffector
Browse files Browse the repository at this point in the history
  • Loading branch information
dragoncoder047 committed Oct 3, 2024
1 parent 9be47a8 commit e17e9b4
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 15 deletions.
75 changes: 75 additions & 0 deletions examples/platformBox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// @ts-check

kaplay();

loadSprite("bean", "/sprites/bean.png");
loadSprite("coin", "/sprites/coin.png");
loadSprite("grass", "/sprites/grass.png");

const SPEED = 480;

setGravity(2400);

const level = addLevel([
"",
"= @ $$ =",
"==========",
], {
tileWidth: 64,
tileHeight: 64,
// Define what each symbol means (in components)
tiles: {
"@": () => [
sprite("bean"),
area(),
body(),
anchor("bot"),
z(2),
"player",
],
"=": () => [
sprite("grass"),
area(),
body({ isStatic: true }),
anchor("bot"),
],
"$": () => [
sprite("coin"),
area(),
body(),
anchor("bot"),
platformEffector({
shouldCollide(obj, normal) {
if (obj !== player) return true;
// Let the player push them if they hold shift
if (isKeyDown("shift")) return true;
if (normal.sub(LEFT).len() < Number.EPSILON) return false;
if (normal.sub(RIGHT).len() < Number.EPSILON) return false;
return true;
}
}),
],
},
});

// Get the player object from tag
const player = level.get("player")[0];

// Movements
onKeyPress("space", () => {
if (player.isGrounded()) {
player.jump(900);
}
});

onKeyDown("left", () => {
player.move(-SPEED, 0);
});

onKeyDown("right", () => {
player.move(SPEED, 0);
});

onUpdate(() => {
camPos(player.worldPos());
});
84 changes: 84 additions & 0 deletions examples/platformEffector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// @ts-check

kaplay();

loadSprite("bean", "/sprites/bean.png");
loadSprite("steel", "/sprites/steel.png");
loadSprite("grass", "/sprites/grass.png");

const SPEED = 480;

setGravity(2400);

const level = addLevel([
" ##",
"",
" ##",
"",
" ##",
"",
" ##",
"",
"====##====",
"",
"= @ ## =",
"==========",
], {
tileWidth: 64,
tileHeight: 64,
// Define what each symbol means (in components)
tiles: {
"@": () => [
sprite("bean"),
area(),
body(),
anchor("bot"),
z(2),
"player",
],
"=": () => [
sprite("grass"),
area(),
body({ isStatic: true }),
anchor("bot"),
],
"#": () => [
sprite("steel"),
area(),
body({ isStatic: true }),
anchor("bot"),
platformEffector(),
],
},
});

// Get the player object from tag
const player = level.get("player")[0];

// Always look at player
onUpdate(() => {
camPos(player.worldPos());
});

// Movements
onKeyPress("space", () => {
if (player.isGrounded()) {
player.jump(900);
}
});

onKeyDown("left", () => {
player.move(-SPEED, 0);
});

onKeyDown("right", () => {
player.move(SPEED, 0);
});

// Fall through when down is pressed
onKeyDown("down", () => {
const p = player.curPlatform();
if (p != null && p.is("platformEffector")) {
p.platformIgnore.add(player);
}
});
51 changes: 37 additions & 14 deletions src/components/physics/effectors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { getGravityDirection } from "../../game";
import { Polygon, Vec2, vec2 } from "../../math";
import type { Comp, GameObj } from "../../types";
import type { PosComp } from "../transform";
Expand Down Expand Up @@ -117,8 +116,8 @@ export function pointEffector(opts: PointEffectorCompOpt): PointEffectorComp {
const forceScale = this.forceMode === "constant"
? 1
: this.forceMode === "inverseLinear"
? 1 / distance
: 1 / distance ** 2;
? 1 / distance
: 1 / distance ** 2;
const force = dir.scale(
this.forceMagnitude * forceScale / length,
);
Expand Down Expand Up @@ -153,31 +152,55 @@ export function constantForce(opts: ConstantForceCompOpt): ConstantForceComp {
}

export type PlatformEffectorCompOpt = {
surfaceArc?: number;
useOneWay?: boolean;
/**
* If the object is moving in a direction in here, it won't collide.
*
* Should be a list of unit vectors `LEFT`, `RIGHT`, `UP`, or `DOWN`.
*/
ignoreSides?: Vec2[]
/**
* A function that determines whether the object should collide.
*
* If present, it overrides the `ignoreSides`; if absent, it is
* automatically created from `ignoreSides`.
*/
shouldCollide?: (obj: GameObj, normal: Vec2) => boolean
};

export interface PlatformEffectorComp extends Comp {
surfaceArc: number;
useOneWay: boolean;
/**
* A set of the objects that should not collide with this, because `shouldCollide` returned true.
*
* Objects in here are automatically removed when the stop colliding, so the casual user shouldn't
* need to touch this much. However, if an object is added to this set before the object collides
* with the platform effector, it won't collide even if `shouldCollide` returns true.
*/
platformIgnore: Set<GameObj>;
}

export function platformEffector(
opt: PlatformEffectorCompOpt,
opt: PlatformEffectorCompOpt = {},
): PlatformEffectorComp {
opt.ignoreSides ??= [Vec2.UP];
opt.shouldCollide ??= (_, normal) => {
return opt.ignoreSides?.findIndex(s => s.unit().sub(normal).slen() < Number.EPSILON) == -1;
}
return {
id: "platformEffector",
require: ["area", "body"],
surfaceArc: opt.surfaceArc ?? 180,
useOneWay: opt.useOneWay ?? false,
platformIgnore: new Set<GameObj>(),
add(this: GameObj<BodyComp | AreaComp | PlatformEffectorComp>) {
this.onBeforePhysicsResolve(collision => {
const v = collision.target.vel;
const up = getGravityDirection().scale(-1);
const angle = up.angleBetween(v);
if (Math.abs(angle) > this.surfaceArc / 2) {
if (this.platformIgnore.has(collision.target)) {
collision.preventResolution();
}
else if (!opt.shouldCollide!(collision.target, collision.normal)) {
collision.preventResolution();
this.platformIgnore.add(collision.target);
}
});
this.onCollideEnd(obj => {
this.platformIgnore.delete(obj);
});
},
};
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ export interface KAPLAYCtx<
* @since v3001.0
* @group Components
*/
platformEffector(options: PlatformEffectorCompOpt): PlatformEffectorComp;
platformEffector(options?: PlatformEffectorCompOpt): PlatformEffectorComp;
/**
* Applies an upwards force (force against gravity) to colliding objects depending on the fluid density and submerged area.
* Good to apply constant thrust.
Expand Down

0 comments on commit e17e9b4

Please sign in to comment.