Search code examples
javascripthtml5-canvastrigonometry2d-games

Why are my angles off but movement works in my 2D, birds-eye view game?


I'm creating a birds-eye view, 2D game in JS using no engine/library (more for the learning challenge). I've got the character movement working with WASD where the character will either go forward (W) or back (S) depending on their current direction (A turns left, D turns right), but when I output the angles (direction) during play-time I'm getting unexpected results.

For example, when a player is facing "up" and I press "W", the player moves up, and the direction that gets output is 90° - as expected. When facing "down" and I press "S", the player moves down, and the direction is 270° - as expected.

But, when facing left, and I press "W", the character does move left but the output direction is , and when facing + moving right, it is 180° - the exact opposite of what I expect.

These are the functions that move my players:

// Turning
    _resetAngle(angle) {
        if (angle >= 360) { return 0 }
        if (angle < 0) { return 359 }
        return angle
    }

    updateDirection(player, keyboardInput) {
        let currentDirection = player.direction
        const turnedLeft = keyboardInput['a']
        const turnedRight = keyboardInput['d']

        if (turnedLeft) { currentDirection -= this.turnAngle }
        if (turnedRight) { currentDirection += this.turnAngle }

        player.setDirection(this._resetAngle(currentDirection))
    }

//Moving
    _calculateNewCoords(movingForward, entity) {
        let newX
        let newY

        // please ignore the code duplication, just testing for now
        if (movingForward) {
            newX = entity.getX() - entity.speed * Math.cos(entity.direction * (Math.PI / 180))
            newY = entity.getY() - entity.speed * Math.sin(entity.direction * (Math.PI / 180))
        }
        else {
            newX = entity.getX() + entity.speed * Math.cos(entity.direction * (Math.PI / 180))
            newY = entity.getY() + entity.speed * Math.sin(entity.direction * (Math.PI / 180))
        }
        return { newX, newY }
    }

    updateCoordinatesByKeyboard(entity, keyboardInput) {
        const movingForward = keyboardInput['w']
        const movingBackwards = keyboardInput['s']

        if ((movingForward && movingBackwards) || !(movingForward || movingBackwards)) { return }
        
        const { newX, newY } = this._calculateNewCoords(movingForward, entity)
        if (this._canMove(entity, newX, newY)) { return entity.setXY(newX, newY) }
    }

And this is the part that renders the players:

drawCharacter(character, image) {
    const scale = this._getScale(character) // for a 'breathing' effect, makes character grow and shrink

    this.context.setTransform(scale, 0, 0, scale, this.windowDimensions.width / 2, this.windowDimensions.height / 2)
    this.context.rotate(character.direction * Math.PI / 180)
    this.context.drawImage(image, -image.width / 2, -image.height / 2)
}

Results when printing player.direction:

Output: 90 (as expected)

Output: 90°

Output: 180 (expect it to be 0)

Output: 180°

Output: 270 (as expected)

Output: 270°

Output: 0 (expect it to be 180)

Output: 0°

Output: 135 (expect it to be 45)

Output: 135°

Output: 315 (expect it to be 225)

Output: 315°

Again, just to reiterate - the players move as expected (i.e. pressing WASD makes the player turn and move correctly) - but the output directions are unexpected, and I'd like to fix that because in the future I'd like to set NPCs at certain angles (i.e. facing 45°) and expect them to face that direction without having to calculate the 'mirror' direction.

Thanks in advance for any help!


Solution

  • I wouldn't say the values you get don't make sense - it's quite the reverse - as it's what I would expect. So it's more of a 'cosmetical' problem as it would be the same for your other game objects.

    Nevertheless you can easily recalculate the output to your desired value by:

    output = Math.abs((input + 90) % 360 - 270)
    

    This will make:

    90 -> 90

    180 -> 0

    270 -> 270

    0 -> 180

    135 -> 45

    315 -> 225