Code Sketch
BHAve,sh art sindoooooor game
Category: Art
// --- OPERATION SINDOOR WITH SLOW ENEMY MISSILES ---
cleari()
originBottomLeft()
val gravity = Vector2D(0, -0.08)
var waveAngle = 0.0
var chakraRotation = 0.0
// ?. ???????? ???????????? ??????????? ??? ???� ????
var missileX = cwidth / 2.0 - 150.0
var missileY = 150.0
var missileAngle = math.Pi / 2.0
class BrahMosMissile(var x: Double, var y: Double, val vx: Double, val vy: Double, val angle: Double) {
var active = true
def step() {
x += vx
y += vy
if (x < -100 || x > cwidth + 100 || y < -100 || y > cheight + 100) active = false
}
def view(canvas: CanvasDraw) {
canvas.pushMatrix()
canvas.translate(x, y)
canvas.rotate(angle)
canvas.fill(200, 200, 200)
canvas.stroke(80, 80, 80)
canvas.strokeWeight(1)
canvas.rect(-15, -6, 30, 12)
canvas.fill(220, 20, 60)
canvas.noStroke()
canvas.ellipse(15, 0, 12, 12)
canvas.fill(50, 50, 50)
canvas.rect(-20, -10, 6, 20)
canvas.fill(255, 140, 0)
canvas.ellipse(-22, 0, 10, 8)
canvas.popMatrix()
}
}
val brahmosShells = ArrayBuffer.empty[BrahMosMissile]
// ?. ??? ?????? ??? ???� ???
var tankX = cwidth / 2.0 + 100.0
val tankY = 30.0
var cannonAngle = math.Pi / 2.0
var muzzleFlashTimer = 0
class TankShell(var x: Double, var y: Double, val vx: Double, val vy: Double) {
var active = true
def step() {
x += vx
y += vy
if (x < -50 || x > cwidth + 50 || y < -50 || y > cheight + 50) active = false
}
def view(canvas: CanvasDraw) {
canvas.fill(255, 140, 0)
canvas.noStroke()
canvas.ellipse(x, y, 9, 9)
canvas.fill(255, 69, 0, 180)
canvas.ellipse(x - vx * 1.5, y - vy * 1.5, 6, 6)
}
}
val tankShells = ArrayBuffer.empty[TankShell]
// ?. ??????? ???????????? (?????? ??? ?????? - Slow Enemy Missile)
class EnemyMissile(var x: Double, var y: Double, var vy: Double) {
var active = true
var exploded = false
var explosionTimer = 0
def step() {
if (!exploded) {
y += vy // ???? ??? ???? ???? (Slow Speed)
if (y < 0) active = false
} else {
explosionTimer += 1
if (explosionTimer > 25) active = false
}
}
def view(canvas: CanvasDraw) {
if (!exploded) {
canvas.pushMatrix()
canvas.translate(x, y)
canvas.rotate(-math.Pi / 2)
canvas.fill(180, 30, 30)
canvas.stroke(80, 10, 10)
canvas.strokeWeight(1)
canvas.rect(-15, -5, 30, 10)
canvas.fill(30, 30, 30)
canvas.noStroke()
canvas.ellipse(15, 0, 10, 10)
canvas.popMatrix()
} else {
canvas.noStroke()
canvas.fill(255, 100, 0, 200)
canvas.ellipse(x, y, 45, 45)
canvas.fill(255, 255, 0, 220)
canvas.ellipse(x, y, 25, 25)
}
}
}
val enemyMissiles = ArrayBuffer.empty[EnemyMissile]
var enemySpawnTimer = 0
// ?. ?????? ????????
val tricolorPalette = Array(
cm.rgb(255, 153, 51),
cm.rgb(255, 255, 255),
cm.rgb(18, 136, 7)
)
class Particle(x0: Double, y0: Double, col: Color, seed: Boolean) {
var location = Vector2D(x0, y0)
private var velocity =
if (seed) Vector2D(0, random(5, 11))
else randomExplosionVector
private var acceleration = Vector2D(0, 0)
var lifespan = 255.0
private var exploded = false
def randomExplosionVector: Vector2D = {
val rv1 = Vector2D(randomNormalDouble, randomNormalDouble)
val r1 = randomDouble(2, 4)
rv1 * r1
}
def applyForce(force: Vector2D) {
acceleration += force
}
def shouldExplode: Boolean = !exploded && lifespan <= 0
def checkExplode() {
if (seed && velocity.y < 0) {
lifespan = 0
}
}
def isDead: Boolean = {
if (seed) exploded else lifespan <= 0
}
def explode() {
exploded = true
}
def step() {
velocity += acceleration
location += velocity
if (!seed) {
lifespan -= 5
velocity *= 0.95
}
acceleration *= 0
checkExplode()
}
def view(canvas: CanvasDraw) {
canvas.stroke(col)
if (seed) canvas.strokeWeight(4) else canvas.strokeWeight(2)
canvas.point(location.x, location.y)
}
}
class Firework() {
private val fireworkColor = tricolorPalette(random(tricolorPalette.length))
private val firework = new Particle(random(cwidth), 0, fireworkColor, true)
private val particles = ArrayBuffer.empty[Particle]
def done: Boolean = firework.isDead && particles.isEmpty
def step() {
particles.filterInPlace(p => !p.isDead)
if (!firework.isDead) {
firework.applyForce(gravity)
firework.step()
if (firework.shouldExplode) {
repeat(80) {
particles.append(new Particle(firework.location.x, firework.location.y, fireworkColor, false))
}
firework.explode()
}
}
repeatFor(particles) { p =>
p.applyForce(gravity)
p.step()
}
}
def view(canvas: CanvasDraw) {
firework.view(canvas)
repeatFor(particles) { p => p.view(canvas) }
}
}
val fireworks = ArrayBuffer.empty[Firework]
def updateState() {
fireworks.filterInPlace(f => !f.done)
if (randomDouble(1) < 0.08) {
fireworks.append(new Firework())
}
repeatFor(fireworks) { f => f.step() }
waveAngle += 0.08
chakraRotation += 0.05
// --- ??? ????????? ---
if (mouseX >= 20 && mouseX <= 75 && mouseY >= 90 && mouseY <= 130) {
tankX -= 2.5
}
else if (mouseX >= 80 && mouseX <= 135 && mouseY >= 90 && mouseY <= 130) {
tankX += 2.5
}
else if (mouseX >= 140 && mouseX <= 195 && mouseY >= 90 && mouseY <= 130) {
cannonAngle -= 0.05
}
else if (mouseX >= 200 && mouseX <= 255 && mouseY >= 90 && mouseY <= 130) {
cannonAngle += 0.05
}
else if (mouseX >= 260 && mouseX <= 325 && mouseY >= 90 && mouseY <= 130) {
val turretBaseX = tankX
val turretBaseY = tankY + 36.0
val barrelLength = 55.0
val tipX = turretBaseX + math.cos(cannonAngle) * barrelLength
val tipY = turretBaseY + math.sin(cannonAngle) * barrelLength
val speed = 7.0
tankShells.append(new TankShell(tipX, tipY, math.cos(cannonAngle) * speed, math.sin(cannonAngle) * speed))
muzzleFlashTimer = 8
}
else if (mouseX >= 335 && mouseX <= 390 && mouseY >= 90 && mouseY <= 130) {
missileAngle -= 0.05
}
else if (mouseX >= 395 && mouseX <= 450 && mouseY >= 90 && mouseY <= 130) {
missileAngle += 0.05
}
else if (mouseX >= 455 && mouseX <= 525 && mouseY >= 90 && mouseY <= 130) {
val mTipX = missileX + math.cos(missileAngle) * 45.0
val mTipY = missileY + math.sin(missileAngle) * 45.0
brahmosShells.append(new BrahMosMissile(mTipX, mTipY, math.cos(missileAngle) * 5.0, math.sin(missileAngle) * 5.0, missileAngle))
}
if (tankX < 50.0) tankX = 50.0
if (tankX > cwidth - 50.0) tankX = cwidth - 50.0
if (muzzleFlashTimer > 0) muzzleFlashTimer -= 1
// ??????? ???????????? ??? ?????????? ??? -0.8 ???? ??? (Very Slow Speed)
enemySpawnTimer += 1
if (enemySpawnTimer >= 110) {
enemySpawnTimer = 0
val ex = random(100, cwidth - 100)
val ey = cheight + 20.0
enemyMissiles.append(new EnemyMissile(ex, ey, -0.8))
}
tankShells.filterInPlace(_.active)
repeatFor(tankShells) { s => s.step() }
brahmosShells.filterInPlace(_.active)
repeatFor(brahmosShells) { bm => bm.step() }
enemyMissiles.filterInPlace(_.active)
repeatFor(enemyMissiles) { em => em.step() }
// ????? ?????? (Collision Detection)
repeatFor(enemyMissiles) { em =>
if (!em.exploded) {
repeatFor(tankShells) { ts =>
if (ts.active && math.abs(ts.x - em.x) < 25 && math.abs(ts.y - em.y) < 25) {
em.exploded = true
ts.active = false
}
}
repeatFor(brahmosShells) { bm =>
if (bm.active && math.abs(bm.x - em.x) < 30 && math.abs(bm.y - em.y) < 30) {
em.exploded = true
bm.active = false
}
}
}
}
}
def drawBrahMosLauncher(canvas: CanvasDraw, mx: Double, my: Double) {
canvas.fill(60, 60, 70)
canvas.stroke(30, 30, 35)
canvas.strokeWeight(2)
canvas.ellipse(mx, my, 44, 44)
canvas.pushMatrix()
canvas.translate(mx, my)
canvas.rotate(missileAngle)
canvas.fill(80, 90, 80)
canvas.stroke(40, 50, 40)
canvas.strokeWeight(2)
canvas.rect(-10, -12, 50, 24)
canvas.fill(120, 30, 30)
canvas.rect(10, -12, 6, 24)
canvas.popMatrix()
}
def drawArmyTank(canvas: CanvasDraw, tx: Double, ty: Double) {
canvas.fill(40, 40, 40)
canvas.stroke(20, 20, 20)
canvas.strokeWeight(1)
canvas.rect(tx - 40, ty, 80, 18)
canvas.fill(80, 80, 80)
for (i <- -3 to 3) {
canvas.ellipse(tx + (i * 11), ty + 9, 8, 8)
}
canvas.fill(55, 85, 45)
canvas.rect(tx - 32, ty + 18, 64, 18)
canvas.fill(40, 70, 35)
canvas.ellipse(tx, ty + 36, 36, 20)
val turretBaseX = tx
val turretBaseY = ty + 36.0
val barrelLength = 55.0
canvas.stroke(30, 55, 25)
canvas.strokeWeight(6)
val tipX = turretBaseX + math.cos(cannonAngle) * barrelLength
val tipY = turretBaseY + math.sin(cannonAngle) * barrelLength
canvas.line(turretBaseX, turretBaseY, tipX, tipY)
if (muzzleFlashTimer > 0) {
canvas.noStroke()
canvas.fill(255, 140, 0, 220)
canvas.ellipse(tipX, tipY, 28, 28)
canvas.fill(255, 235, 0, 250)
canvas.ellipse(tipX, tipY, 16, 16)
canvas.fill(255, 255, 255)
canvas.ellipse(tipX, tipY, 8, 8)
}
}
def drawControlButtons(canvas: CanvasDraw) {
canvas.fill(50, 50, 80, 200)
canvas.stroke(200, 200, 200)
canvas.strokeWeight(2)
canvas.rect(20, 90, 55, 40)
canvas.fill(255, 255, 255)
canvas.text("?", 38, 100)
canvas.fill(50, 50, 80, 200)
canvas.stroke(200, 200, 200)
canvas.strokeWeight(2)
canvas.rect(80, 90, 55, 40)
canvas.fill(255, 255, 255)
canvas.text("?", 98, 100)
canvas.fill(80, 50, 50, 200)
canvas.stroke(200, 200, 200)
canvas.strokeWeight(2)
canvas.rect(140, 90, 55, 40)
canvas.fill(255, 255, 255)
canvas.text("?T", 155, 100)
canvas.fill(80, 50, 50, 200)
canvas.stroke(200, 200, 200)
canvas.strokeWeight(2)
canvas.rect(200, 90, 55, 40)
canvas.fill(255, 255, 255)
canvas.text("?T", 215, 100)
canvas.fill(200, 80, 0, 220)
canvas.stroke(255, 255, 255)
canvas.strokeWeight(2)
canvas.rect(260, 90, 65, 40)
canvas.fill(255, 255, 255)
canvas.text("FIRE T", 270, 100)
canvas.fill(100, 40, 120, 200)
canvas.stroke(200, 200, 200)
canvas.strokeWeight(2)
canvas.rect(335, 90, 55, 40)
canvas.fill(255, 255, 255)
canvas.text("?M", 350, 100)
canvas.fill(100, 40, 120, 200)
canvas.stroke(200, 200, 200)
canvas.strokeWeight(2)
canvas.rect(395, 90, 55, 40)
canvas.fill(255, 255, 255)
canvas.text("?M", 410, 100)
canvas.fill(150, 0, 200, 220)
canvas.stroke(255, 255, 255)
canvas.strokeWeight(2)
canvas.rect(455, 90, 70, 40)
canvas.fill(255, 255, 255)
canvas.text("LAUNCH", 462, 100)
}
def drawWavingTricolorFlag(canvas: CanvasDraw) {
val fw = 160.0
val fh = 28.0
val fx = (cwidth - fw) / 2.0 + 110.0
val fy = cheight / 2.0 - 10.0
canvas.stroke(200, 200, 200)
canvas.strokeWeight(5)
canvas.line(fx, fy + fh * 3 + 15, fx, fy - 160)
val stepSize = 4.0
var x = 0.0
while (x < fw) {
val yOffset = math.sin(waveAngle + (x * 0.03)) * 8.0
canvas.stroke(255, 153, 51)
canvas.strokeWeight(stepSize + 1)
canvas.line(fx + x, fy + fh * 2 + yOffset, fx + x, fy + fh * 3 + yOffset)
canvas.stroke(255, 255, 255)
canvas.line(fx + x, fy + fh + yOffset, fx + x, fy + fh * 2 + yOffset)
canvas.stroke(18, 136, 7)
canvas.line(fx + x, fy + yOffset, fx + x, fy + fh + yOffset)
x += stepSize
}
val chakraX = fx + fw / 2.0
val chakraY = fy + fh + (fh / 2.0) + (math.sin(waveAngle + ((fw / 2.0) * 0.03)) * 8.0)
val radius = 11.0
canvas.stroke(0, 0, 128)
canvas.strokeWeight(2)
canvas.noFill()
canvas.ellipse(chakraX, chakraY, radius * 2, radius * 2)
canvas.strokeWeight(1)
for (i <- 0 until 24) {
val angle = chakraRotation + (i * (2 * math.Pi / 24))
val xEnd = chakraX + math.cos(angle) * radius
val yEnd = chakraY + math.sin(angle) * radius
canvas.line(chakraX, chakraY, xEnd, yEnd)
}
}
def drawBigOperationSindoorTitle(canvas: CanvasDraw) {
val startX = 25.0
val startY = cheight - 60.0
canvas.fill(160, 160, 160)
canvas.text("OPERATION", startX, startY)
canvas.fill(220, 20, 20)
canvas.text("SINDOOR", startX, startY - 35.0)
}
def viewState(canvas: CanvasDraw) {
canvas.fill(10, 10, 25, 45)
canvas.noStroke()
canvas.rect(0, 0, cwidth, cheight)
repeatFor(fireworks) { f => f.view(canvas) }
drawBrahMosLauncher(canvas, missileX, missileY)
drawArmyTank(canvas, tankX, tankY)
drawControlButtons(canvas)
repeatFor(tankShells) { s => s.view(canvas) }
repeatFor(brahmosShells) { bm => bm.view(canvas) }
repeatFor(enemyMissiles) { em => em.view(canvas) }
drawBigOperationSindoorTitle(canvas)
drawWavingTricolorFlag(canvas)
}
animateWithCanvasDraw { canvas =>
updateState()
viewState(canvas)
}