Last active
March 14, 2019 14:46
-
-
Save Willmo36/43917784e1ae201f2a8f444ce3ad9800 to your computer and use it in GitHub Desktop.
Basic trigonometry via tiny game
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 GameState = { | |
keyCode: number | null; | |
playerPos: Pair; | |
playerImg: HTMLImageElement; | |
enemyPos: Pair; | |
enemyImg: HTMLImageElement; | |
}; | |
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, | |
playerPos: [100, 100], | |
playerImg, | |
enemyPos: [400, 300], | |
enemyImg | |
}; | |
const runLoop = () => { | |
requestAnimationFrame(() => loop(gs, context, runLoop)); | |
}; | |
runLoop(); | |
} | |
function loop(gs: GameState, ctx: CanvasRenderingContext2D, done: Function) { | |
ctx.clearRect(0, 0, 500, 500); | |
gs.playerPos = nextPlayerPosition(gs); | |
//TRIG PART | |
//knowing 2/3 points (player & enemy) we can find the right angle point | |
let ra: Pair = [gs.playerPos[0], gs.enemyPos[1]]; | |
//then we can figure out the opposite and adjacent lengths | |
let oppositeL = Math.max(gs.playerPos[1], ra[1]) - Math.min(gs.playerPos[1], ra[1]); | |
let adjacentL = Math.max(gs.enemyPos[0], ra[0]) - Math.min(gs.enemyPos[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.playerPos[1] < gs.enemyPos[1] ? Math.PI : 0; | |
//then use the beta to face the enemy | |
const latRotation = beta * (gs.playerPos[1] < gs.enemyPos[1] ? -1 : 1); | |
const playerDimensions: Pair = [50, 50]; | |
const playerOffsets: Pair = [playerDimensions[0] / 2, playerDimensions[1] / 2]; | |
renderRightTriangle(ctx, gs, ra); | |
renderPlayer(ctx, gs, hozRotation, latRotation, playerOffsets, playerDimensions); | |
renderEnemy(ctx, gs); | |
done(); | |
} | |
function renderEnemy(ctx: CanvasRenderingContext2D, gs: GameState) { | |
const enemyDimensions = [150, 150]; | |
const enemyOffsets = [enemyDimensions[0] / 2, enemyDimensions[1] / 2]; | |
ctx.drawImage( | |
gs.enemyImg, | |
gs.enemyPos[0] - enemyOffsets[0], | |
gs.enemyPos[1] - enemyOffsets[1], | |
enemyDimensions[0], | |
enemyDimensions[1] | |
); | |
} | |
function renderPlayer( | |
ctx: CanvasRenderingContext2D, | |
gs: GameState, | |
hozRotation: number, | |
latRotation: number, | |
playerOffsets: Pair, | |
playerDimensions: Pair | |
) { | |
ctx.save(); | |
ctx.translate(gs.playerPos[0], gs.playerPos[1]); | |
ctx.rotate(hozRotation + latRotation); | |
ctx.drawImage( | |
gs.playerImg, | |
-playerOffsets[0], | |
-playerOffsets[1], | |
playerDimensions[0], | |
playerDimensions[1] | |
); | |
ctx.restore(); | |
} | |
function renderRightTriangle(ctx: CanvasRenderingContext2D, gs: GameState, ra: Pair) { | |
ctx.save(); | |
ctx.beginPath(); | |
ctx.strokeStyle = "red"; | |
ctx.moveTo(gs.playerPos[0], gs.playerPos[1]); | |
ctx.lineTo(ra[0], ra[1]); | |
ctx.stroke(); | |
ctx.moveTo(gs.enemyPos[0], gs.enemyPos[1]); | |
ctx.lineTo(ra[0], ra[1]); | |
ctx.stroke(); | |
ctx.moveTo(gs.enemyPos[0], gs.enemyPos[1]); | |
ctx.lineTo(gs.playerPos[0], gs.playerPos[1]); | |
ctx.stroke(); | |
ctx.closePath(); | |
ctx.restore(); | |
} | |
function nextPlayerPosition(gs: GameState): Pair { | |
const changes = movement.get(gs.keyCode || 0) || [0, 0]; | |
return [gs.playerPos[0] + changes[0], gs.playerPos[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 14, 2019
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment