2.2.8 Cycle 8 Wind
Design
The aim of this cycle is to add randomisation to the game even further in the form of a wind force which affects the trajectory of the bullet when it is in the air.
Objectives
Key functions
addWind
Generate a random force between 2 boundaries to be used as a wind force
Development
Outcome
import "kaboom/global"
import kaboom from "kaboom"
import Terrain from "./terrain-generate.js"
export const WIDTH = 1920
export const HEIGHT = 1080
const GRAVITY = 450
kaboom({
width: WIDTH,
height: HEIGHT,
background: [69, 65, 65]
})
/*
const recording = record()
onKeyDown(".", () => {
recording.download({filename: "recording"})
})
*/
setGravity(GRAVITY)
loadBean()
loadPedit("barrel", "sprites/barrel.pedit")
loadPedit("bullet", "sprites/bullet.pedit")
loadPedit("arrow", "sprites/arrow.pedit")
function addBtn(txt, p, f) {
// add a parent background object
const btn = add([
rect(240, 80, { radius: 8 }),
pos(p),
area(),
scale(1),
anchor("center"),
outline(4),
])
// add a child object that displays the text
btn.add([
text(txt),
anchor("center"),
color(0, 0, 0),
])
// onHoverUpdate() comes from area() component
// it runs every frame when the object is being hovered
btn.onHoverUpdate(() => {
btn.scale = vec2(1.2)
setCursor("pointer")
})
// onHoverEnd() comes from area() component
// it runs once when the object stopped being hovered
btn.onHoverEnd(() => {
btn.scale = vec2(1)
setCursor("default")
})
// onClick() comes from area() component
// it runs once when the object is clicked
btn.onClick(f)
return btn
}
const addWind = () => {
//generate random number (+ or -) for wind force
//display wind
//add the force to the bullet either through the move command of the bullet or by detecting when the bullet is high up
const minWind = -2
const windVariance = 5
let windForce = minWind + Math.floor(Math.random() * windVariance)
add([
text(Math.abs(windForce).toString()),
pos(176, 24)
])
add([
sprite("arrow"),
pos(186, 20),
rotate(windForce < 0 ? 180 : 0),
anchor("center")
])
return windForce
}
const newBullet = (position, angle, power) => {
return add([
sprite("bullet"),
pos(position),
//offscreen({ destroy: true }),
area(),
anchor("center"),
body(),
rotate(angle),
move(vec2(Math.cos(angle / 180 * Math.PI), Math.sin(angle / 180 * Math.PI)), (power + 20) * 8.3), //Calculating movement vector
"bullet"
])
}
scene("practice", () => {
onDraw(() => {
Terrain.drawTerrain()
})
Terrain.seedTerrain()
Terrain.interpolateLinear()
Terrain.tCollision()
const wind = addWind()
onLoad(() => {
add([
pos(250, 250),
text("Press \"esc\" to return to menu"),
lifespan(2, { fade: 0.5 })
])
})
const bean = add([
sprite("bean"),
pos(80, HEIGHT - (Terrain.points[80] + 27)),
rotate(0),
anchor("center"),
{
SPEED_HIGH: 150,
SPEED_LOW: 45,
power: 0
}
])
const target = add([
sprite("bean"),
pos(1500, HEIGHT - (Terrain.points[1500] + 27)),
area(),
anchor("center")
])
bean.add([
sprite("barrel"),
scale(3, 3),
pos(0, -60)
])
target.onCollide("bullet", () => {
addKaboom(target.pos)
destroy(target)
destroyAll("bullet")
go("practice")
})
onCollide("bullet", "ground", () => {
destroyAll("bullet")
})
target.onCollide("nuke", () => {
addKaboom(target.pos, {scale: 15})
destroy(target)
destroyAll("nuke")
play("explosion")
wait(3, () => {go("practice")})
})
const angle = add([ //UI for displaying the angle of player
text(Math.floor(bean.angle).toString()),
pos(24, 24)
])
const power = add([ //UI to display power of the shot
text(Math.floor(bean.power).toString()),
pos(100, 24)
])
let bullet
onKeyDown("space", () => {
if (get("bullet").length == 0) {
let endOfBarrel = vec2(bean.pos.x + 85 * Math.cos(-0.15 + bean.angle / 180 * Math.PI), bean.pos.y + 92 * Math.sin(-0.15 + bean.angle / 180 * Math.PI)) //Find the position of the end of the barrel to add bullets
bullet = newBullet(endOfBarrel, bean.angle, bean.power)
}
})
onUpdate(() => {
angle.text = Math.abs(Math.floor(bean.angle)).toString() //Update player angle UI
power.text = Math.floor(bean.power).toString()
if (get("bullet").length > 0) {
if (bullet.pos.x > WIDTH || bullet.pos.x < 0) {
destroy(bullet)
}
if (bullet.pos.y < 300) {
bullet.pos.x += wind
}
}
})
let rotationSpeed = bean.SPEED_HIGH
onKeyDown("alt", () => {
rotationSpeed = bean.SPEED_LOW //Slow rotation when holding alt
})
onKeyRelease("alt", () => {
rotationSpeed = bean.SPEED_HIGH //Normal when alt released
})
onKeyDown("a", () => {
if (bean.pos.x > 0) {
bean.pos.x--
bean.pos.y = HEIGHT - Terrain.points[bean.pos.x] - 27
}
})
onKeyDown("d", () => {
if (bean.pos.x < WIDTH / 3) {
bean.pos.x++
bean.pos.y = HEIGHT - Terrain.points[bean.pos.x] - 27
}
})
onKeyDown("left", () => {
if (bean.angle > -70) {
bean.angle += -rotationSpeed * dt() //Rotate left
}
else {
bean.angle = -70 //Stop at 70 degrees anticlockwise
}
})
onKeyDown("right", () => {
if (bean.angle < 0) {
bean.angle += rotationSpeed * dt() //Rotate right
}
else {
bean.angle = 0 //Prevent tank rotating the wrong direction
}
})
let smooth = 3;
onKeyDown("up", () => {
smooth += 1.2 * dt()
if (bean.power < 100) {
bean.power += 0.7 * smooth ** 2 * dt() //Increase power smoothly
}
else {
bean.power = 100 //Maximum power 100
}
})
onKeyRelease("up", () => {
smooth = 3
})
onKeyDown("down", () => {
smooth += 1.2 * dt()
if (bean.power > 1) {
bean.power -= 0.7 * smooth ** 2 * dt() //Decrease power smoothly
}
else {
bean.power = 0 //Minimum power 0
}
})
onKeyRelease("down", () => {
smooth = 3
})
const cheatInputs = ["up","up","down","down","left","right","left","right","space"]
let inputs = []
const cheatCode = (key) => {
inputs.push(key)
if (cheatInputs.slice(0,inputs.length).toString() == inputs.toString()) {
if (inputs.length == cheatInputs.length){
inputs = []
dropNuke(0, -300)
}
}
else {
inputs = []
}
}
const dropNuke = (x, y) => {
loadSprite("nuke", "/sprites/nuke.png")
loadSound("explosion", "/sounds/explosion.mp3")
const nuke = add([
sprite("nuke"),
rotate(target.pos.angle(vec2(x,y))),
scale(0.7),
pos(x, y),
anchor("center"),
area(),
move(target.pos.angle(vec2(x,y)), 1200),
"nuke"
])
}
onKeyPress((key) => {
cheatCode(key)
})
/*onKeyPress("up", () => {})
onKeyPress("down", () => {})
onKeyPress("left", () => {})
onKeyPress("right", () => {})*/
onKeyDown("escape", () => {
go("main-menu")
})
})
scene("multiplayer-menu", () => {
addBtn("Join Game", vec2(250, 150), () => { })
addBtn("Create Game", vec2(250, 250), () => { })
addBtn("Return", vec2(250, 350), () => { go("main-menu") })
})
scene("main-menu", () => {
addBtn("Practice", vec2(250, 250), () => { go("practice") })
addBtn("Multiplayer", vec2(250, 350), () => { go("multiplayer-menu") })
})
go("main-menu")Challenges
The one challenge I had encountered while developing this cycle is that some levels seemed impossible during playthroughs so I had to make the player able to move along the terrain to make shots easier.
Testing
Tests
1
Run code
Kaboom should run
Kaboom ran normally
Pass
2
Enter "practice"
Wind should be displayed in the UI
Wind force is shown with an arrow to show direction
Pass
3
Shoot a bullet in the air
Bullet should be affected by wind when in the air
Works as expected
Pass
4
Press "a" and "d" keys
Player should move left and right along the terrain
Works as expected
Pass
Evidence

Last updated