raylib-wasm
Version:
Make raylib games in JS for the web
250 lines (213 loc) • 7.34 kB
JavaScript
const G = 400
const PLAYER_JUMP_SPD = 350
const PLAYER_HOR_SPD = 200
const screenWidth = 800
const screenHeight = 450
const envItems = []
let camera
const player = {
canJump: false,
speed: 0
}
let playerRect
// these are statics (shared between calls) used in camera functions
let evenOutSpeed = 700
let eveningOut = false
let evenOutTarget
let bbox
const updateFunctions = {
"Follow player center": (delta) => {
camera.offset.x = screenWidth / 2
camera.offset.y = screenHeight / 2
camera.target.x = player.position.x
camera.target.y = player.position.y
},
"Follow player center, but clamp to map edges": (delta) => {
camera.target.x = player.position.x
camera.target.y = player.position.y
camera.offset.x = screenWidth / 2
camera.offset.y = screenHeight / 2
let minX = 1000
let minY = 1000
let maxX = -1000
let maxY = -1000
for (const ei of envItems) {
minX = Math.min(ei.rect.x, minX)
maxX = Math.max(ei.rect.x + ei.rect.width, maxX)
minY = Math.min(ei.rect.y, minY)
maxY = Math.max(ei.rect.y + ei.rect.height, maxY)
}
const v = new Vector2({ x:maxX, y:maxY })
const max = GetWorldToScreen2D(v, camera)
v.x = minX
v.y = minY
const min = GetWorldToScreen2D(v, camera)
free(v)
if (max.x < screenWidth) {
camera.offset.x = screenWidth - (max.x - screenWidth/2)
}
if (max.y < screenHeight) {
camera.offset.y = screenHeight - (max.y - screenHeight/2)
}
if (min.x > 0) {
camera.offset.x = (screenWidth/2) - min.x
}
if (min.y > 0) {
camera.offset.y = (screenHeight/2) - min.y
}
free(min)
free(max)
},
"Follow player center; smoothed": (delta) => {
const minSpeed = 30
const minEffectLength = 10
const fractionSpeed = 0.8
camera.offset.x = screenWidth / 2
camera.offset.y = screenHeight / 2
const diff = Vector2Subtract(player.position, camera.target);
const length = Vector2Length(diff)
if (length > minEffectLength) {
const speed = Math.max(fractionSpeed*length, minSpeed)
const v = Vector2Add(camera.target, Vector2Scale(diff, speed*delta/length));
camera.target.x = v.x
camera.target.y = v.y
free(v)
}
free(diff)
},
"Follow player center horizontally; update player center vertically after landing": (delta) => {
camera.offset.x = screenWidth / 2
camera.offset.y = screenHeight / 2
camera.target.x = player.position.x
if (eveningOut) {
if (evenOutTarget > camera.target.y) {
camera.target.y += evenOutSpeed*delta
if (camera.target.y > evenOutTarget) {
camera.target.y = evenOutTarget
eveningOut = false
}
} else {
camera.target.y -= evenOutSpeed*delta;
if (camera.target.y < evenOutTarget) {
camera.target.y = evenOutTarget
eveningOut = false
}
}
} else {
if (player.canJump && (player.speed == 0) && (player.position.y != camera.target.y)) {
eveningOut = true
evenOutTarget = player.position.y
}
}
},
// TODO: this one seems broke
"Player push camera on getting too close to screen edge": (delta) => {
const v = new Vector2({ x:(1 - bbox.x) * 0.5 * screenWidth, y: (1 - bbox.y) * 0.5 * screenHeight})
const bboxWorldMin = GetScreenToWorld2D(v, camera)
v.x = (1 + bbox.x) * 0.5 * screenWidth
v.y = (1 + bbox.y) * 0.5 * screenHeight
const bboxWorldMax = GetScreenToWorld2D(v, camera)
camera.offset.x = (1 - bbox.x) * 0.5 * screenWidth
camera.offset.y = (1 - bbox.y) * 0.5 * screenHeight
if (player.position.x < bboxWorldMin.x){
camera.target.x = player.position.x
}
if (player.position.y < bboxWorldMin.y){
camera.target.y = player.position.y
}
if (player.position.x > bboxWorldMax.x){
camera.target.x = bboxWorldMin.x + (player.position.x - bboxWorldMax.x)
}
if (player.position.y > bboxWorldMax.y) {
camera.target.y = bboxWorldMin.y + (player.position.y - bboxWorldMax.y)
}
free(v)
free(bboxWorldMin)
free(bboxWorldMax)
}
}
let cameraOption = 0
const cameraUpdaters = Object.values(updateFunctions)
const cameraDescriptions = Object.keys(updateFunctions)
function UpdatePlayer(delta) {
if (IsKeyDown(KEY_LEFT)) {
player.position.x -= (PLAYER_HOR_SPD * delta)
}
if (IsKeyDown(KEY_RIGHT)) {
player.position.x += (PLAYER_HOR_SPD * delta)
}
if (IsKeyDown(KEY_SPACE) && player.canJump) {
player.speed = -PLAYER_JUMP_SPD
player.canJump = false
}
let hitObstacle = false
for (const ei of envItems) {
if (
ei.blocking &&
ei.rect.x <= player.position.x &&
ei.rect.x + ei.rect.width >= player.position.x &&
ei.rect.y >= player.position.y &&
ei.rect.y <= player.position.y + player.speed * delta
) {
hitObstacle = true
player.speed = 0
player.position.y = ei.rect.y
}
}
if (!hitObstacle) {
player.position.y += player.speed * delta
player.speed += G*delta
player.canJump = false
} else {
player.canJump = true
}
}
const InitGame = async () => {
InitWindow(screenWidth, screenHeight)
player.position = new Vector2({ x: 400, y: 280})
camera = new Camera2D({ position: player.position, offset: new Vector2({ x: screenWidth/2, y: screenHeight/2 }), rotation: 0, zoom: 1 })
envItems.push({ rect: new Rectangle({ x: 0, y:0, width:1000, height:400 }), blocking: false, color: LIGHTGRAY })
envItems.push({ rect: new Rectangle({ x: 0, y:400, width:1000, height:200 }), blocking: true, color: GRAY })
envItems.push({ rect: new Rectangle({ x: 300, y:200, width:400, height:10 }), blocking: true, color: GRAY })
envItems.push({ rect: new Rectangle({ x: 250, y:300, width:100, height:10 }), blocking: true, color: GRAY })
envItems.push({ rect: new Rectangle({ x: 650, y:300, width:100, height:10 }), blocking: true, color: GRAY })
playerRect = new Rectangle({ x: player.position.x - 20, y: player.position.y - 40, width: 40, height: 40 })
bbox = new Vector2({ x:2, y:2 })
}
const UpdateGame = (ts) => {
const delta = GetFrameTime()
UpdatePlayer(delta)
camera.zoom += (GetMouseWheelMove() * 0.05)
if (camera.zoom > 3) {
camera.zoom = 3
} else if (camera.zoom < 0.25) {
camera.zoom = 0.25
}
if (IsKeyPressed(KEY_R)) {
camera.zoom = 1
player.position.x = 400
player.position.y = 280
}
if (IsKeyPressed(KEY_C)){
cameraOption = (cameraOption + 1) % cameraUpdaters.length
}
cameraUpdaters[cameraOption](delta)
playerRect.x = player.position.x - 20
playerRect.y = player.position.y - 40
BeginDrawing()
ClearBackground(LIGHTGRAY)
BeginMode2D(camera)
for (const env of envItems) {
DrawRectangleRec(env.rect, env.color)
}
DrawRectangleRec(playerRect, RED)
EndMode2D()
DrawText("Controls:", 20, 20, 10, BLACK)
DrawText("- Right/Left to move", 40, 40, 10, DARKGRAY)
DrawText("- Space to jump", 40, 60, 10, DARKGRAY)
DrawText("- Mouse Wheel to Zoom in-out, R to reset zoom", 40, 80, 10, DARKGRAY)
DrawText("- C to change camera mode", 40, 100, 10, DARKGRAY)
DrawText("Current camera mode:", 20, 120, 10, BLACK)
DrawText(cameraDescriptions[cameraOption], 40, 140, 10, DARKGRAY)
EndDrawing()
}