Created
March 15, 2019 16:48
-
-
Save Willmo36/116e6884e4b2b3b89d5af767591a56d7 to your computer and use it in GitHub Desktop.
Basic trigonometry via tiny game - Collision detection
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); |
Author
Willmo36
commented
Mar 15, 2019
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment