Skip to content

Instantly share code, notes, and snippets.

@Willmo36
Created March 15, 2019 16:48
Show Gist options
  • Save Willmo36/116e6884e4b2b3b89d5af767591a56d7 to your computer and use it in GitHub Desktop.
Save Willmo36/116e6884e4b2b3b89d5af767591a56d7 to your computer and use it in GitHub Desktop.
Basic trigonometry via tiny game - Collision detection
type Pair = [number, number];
type Entity = {
dimensions: Pair;
pos: Pair;
img: HTMLImageElement;
collider: number;
};
type GameState = {
keyCode: number | null;
player: Entity;
enemy: Entity;
};
function game() {
//SETUP
let canvas = document.getElementById("canvas") as HTMLCanvasElement;
let context = canvas.getContext("2d")!;
canvas.width = 750;
canvas.height = 500;
let playerImg = new Image();
playerImg.src = "/assets/player.png";
let enemyImg = new Image();
enemyImg.src = "/assets/enemy.png";
document.addEventListener("keydown", e => {
gs.keyCode = e.keyCode;
});
document.addEventListener("keyup", () => {
gs.keyCode = null;
});
let gs: GameState = {
keyCode: null,
player: {
pos: [100, 100],
img: playerImg,
collider: 20.0,
dimensions: [50, 50]
},
enemy: {
pos: [400, 300],
img: enemyImg,
collider: 75,
dimensions: [150, 150]
}
};
const runLoop = () => {
requestAnimationFrame(() => loop(gs, context, runLoop));
};
runLoop();
}
function loop(gs: GameState, ctx: CanvasRenderingContext2D, done: Function) {
//next version click on a spot and it moves to there?
ctx.clearRect(0, 0, 750, 500);
gs.player.pos = nextPlayerPosition(gs);
//TRIG PART
//knowing 2/3 points (player & enemy) we can find the right angle point
let ra: Pair = [gs.player.pos[0], gs.enemy.pos[1]];
//then we can figure out the opposite and adjacent lengths
let oppositeL = Math.max(gs.player.pos[1], ra[1]) - Math.min(gs.player.pos[1], ra[1]);
let adjacentL = Math.max(gs.enemy.pos[0], ra[0]) - Math.min(gs.enemy.pos[0], ra[0]);
//from these we can reverse tan fn to get the alpha angle (angle at the enemys side)
const alpha = Math.atan(oppositeL / adjacentL);
//using alpha we can figure out the beta, the angle on the players side
const beta = Math.PI - Math.PI / 2 - alpha;
//Have the player face the right angle so we can use the beta radians
const hozRotation = gs.player.pos[1] < gs.enemy.pos[1] ? Math.PI : 0;
//then use the beta to face the enemy
const latRotation = beta * (gs.player.pos[1] < gs.enemy.pos[1] ? -1 : 1);
const collided = collisionDectection(gs.player, gs.enemy);
renderRightTriangle(ctx, gs, ra);
renderEntity(ctx, gs.player, hozRotation + latRotation);
renderEntity(ctx, gs.enemy, 0);
renderCollider(ctx, gs.player.pos, gs.player.collider, collided);
renderCollider(ctx, gs.enemy.pos, gs.enemy.collider, collided);
done();
}
function renderEntity(ctx: CanvasRenderingContext2D, entity: Entity, rotation: number) {
const offsets: Pair = [entity.dimensions[0] / 2, entity.dimensions[1] / 2];
ctx.save();
ctx.translate(entity.pos[0], entity.pos[1]);
ctx.rotate(rotation);
ctx.drawImage(entity.img, -offsets[0], -offsets[1], entity.dimensions[0], entity.dimensions[1]);
ctx.restore();
}
function renderCollider(
ctx: CanvasRenderingContext2D,
coord: Pair,
radius: number,
collided: boolean
) {
ctx.save();
ctx.beginPath();
ctx.strokeStyle = collided ? "red" : "green";
ctx.arc(coord[0], coord[1], radius, 0, Math.PI * 2);
ctx.stroke();
ctx.closePath();
ctx.restore();
}
function renderRightTriangle(ctx: CanvasRenderingContext2D, gs: GameState, ra: Pair) {
ctx.save();
ctx.beginPath();
ctx.strokeStyle = "red";
ctx.moveTo(gs.player.pos[0], gs.player.pos[1]);
ctx.lineTo(ra[0], ra[1]);
ctx.stroke();
ctx.moveTo(gs.enemy.pos[0], gs.enemy.pos[1]);
ctx.lineTo(ra[0], ra[1]);
ctx.stroke();
ctx.moveTo(gs.enemy.pos[0], gs.enemy.pos[1]);
ctx.lineTo(gs.player.pos[0], gs.player.pos[1]);
ctx.stroke();
ctx.closePath();
ctx.restore();
}
function collisionDectection(a: Entity, b: Entity): boolean {
//find the hypotenuse length between
//check if hyp length > a.col + b.col
const deltaX = a.pos[0];
const deltaY = b.pos[1];
const oppositeL = Math.max(a.pos[1], deltaY) - Math.min(a.pos[1], deltaY);
const adjacentL = Math.max(b.pos[0], deltaX) - Math.min(b.pos[0], deltaX);
//Pythagorus to get the hypotenuse
const hypotenuseL = Math.sqrt(Math.pow(oppositeL, 2) + Math.pow(adjacentL, 2));
const collided = hypotenuseL < a.collider + b.collider;
return collided;
}
function nextPlayerPosition(gs: GameState): Pair {
const changes = movement.get(gs.keyCode || 0) || [0, 0];
return [gs.player.pos[0] + changes[0], gs.player.pos[1] + changes[1]];
}
const UP = 38;
const DOWN = 40;
const LEFT = 37;
const RIGHT = 39;
const speed = 2;
const movement = new Map<number, Pair>([
[UP, [0, -speed]],
[DOWN, [0, speed]],
[LEFT, [-speed, 0]],
[RIGHT, [speed, 0]]
]);
game();
@Willmo36
Copy link
Author

image
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment