Code Sketch
siddhi sabale
Category: Programming
// ============================================================
// ? TITAN X-1 // ULTRA ROBOT
// CINEMATIC MOUSE CONTROLLED WALKING ANIMATION
// KOJO / SCALA
// ============================================================
cleari()
originBottomLeft()
class Sketch {
// ==========================================================
// ROBOT POSITION
// ==========================================================
var rx = cwidth / 2.0
var ry = 170.0
var targetX = cwidth / 2.0
var targetY = 170.0
var walkPhase = 0.0
var idlePhase = 0.0
var glowPhase = 0.0
var blinkTimer = 0.0
var lastX = cwidth / 2.0
var speedX = 0.0
var dustTimer = 0.0
// ==========================================================
// PARTICLE DATA
// ==========================================================
case class Dust(
var x: Double,
var y: Double,
var vx: Double,
var vy: Double,
var life: Double,
var size: Double
)
val dust = scala.collection.mutable.ArrayBuffer.empty[Dust]
// ==========================================================
// SAFE HELPERS
// ==========================================================
def clamp(v: Double, lo: Double, hi: Double): Double = {
math.max(lo, math.min(hi, v))
}
def alphaValue(v: Double): Int = {
clamp(v, 0.0, 255.0).toInt
}
// ==========================================================
// DISTANCE
// ==========================================================
def distance(
x1: Double,
y1: Double,
x2: Double,
y2: Double
): Double = {
math.sqrt(
(x2 - x1) * (x2 - x1) +
(y2 - y1) * (y2 - y1)
)
}
// ==========================================================
// GLOW DOT
// ==========================================================
def glowDot(
c: CanvasDraw,
x: Double,
y: Double,
size: Double,
phase: Double
): Unit = {
val a = alphaValue(
70.0 + math.sin(phase) * 45.0
)
c.noStroke()
c.fill(0, 180, 255, a)
c.ellipse(x, y, size * 2.5, size * 2.5)
c.fill(120, 240, 255, 220)
c.ellipse(x, y, size, size)
}
// ==========================================================
// METAL JOINT
// ==========================================================
def joint(
c: CanvasDraw,
x: Double,
y: Double,
r: Double
): Unit = {
c.fill(115, 125, 138)
c.stroke(55, 65, 75)
c.strokeWeight(1.4)
c.ellipse(x, y, r * 2.0, r * 2.0)
c.fill(205, 215, 225)
c.noStroke()
c.ellipse(
x - r * 0.22,
y + r * 0.22,
r * 0.95,
r * 0.95
)
c.fill(35, 150, 235)
c.ellipse(
x,
y,
r * 0.42,
r * 0.42
)
}
// ==========================================================
// LIMB SEGMENT
// ==========================================================
def limb(
c: CanvasDraw,
x1: Double,
y1: Double,
x2: Double,
y2: Double,
thickness: Double
): Unit = {
c.stroke(0, 0, 0, 70)
c.strokeWeight(thickness + 4.0)
c.line(x1 + 2, y1 - 2, x2 + 2, y2 - 2)
c.stroke(238, 241, 246)
c.strokeWeight(thickness)
c.line(x1, y1, x2, y2)
c.stroke(185, 192, 204)
c.strokeWeight(2.0)
val mx = (x1 + x2) / 2.0
val my = (y1 + y2) / 2.0
c.line(
x1,
y1,
mx,
my
)
}
// ==========================================================
// BLUE ARMOR PANEL
// ==========================================================
def armor(
c: CanvasDraw,
x: Double,
y: Double,
w: Double,
h: Double
): Unit = {
c.fill(4, 36, 88)
c.stroke(15, 145, 235)
c.strokeWeight(1.8)
c.ellipse(x, y, w, h)
c.fill(35, 145, 235, 130)
c.noStroke()
c.ellipse(
x - w * 0.18,
y + h * 0.14,
w * 0.42,
h * 0.24
)
c.fill(110, 220, 255, 120)
c.ellipse(
x - w * 0.22,
y + h * 0.12,
w * 0.18,
h * 0.12
)
}
// ==========================================================
// FOOT
// ==========================================================
def drawFoot(
c: CanvasDraw,
x: Double,
y: Double,
flip: Double
): Unit = {
c.fill(243, 245, 248)
c.stroke(70, 80, 90)
c.strokeWeight(2)
c.ellipse(
x + flip * 5.0,
y,
30,
14
)
c.fill(4, 48, 110)
c.noStroke()
c.ellipse(
x + flip * 5.0,
y - 5,
23,
6
)
c.fill(50, 175, 245, 130)
c.ellipse(
x + flip * 7.0,
y - 4,
10,
3
)
}
// ==========================================================
// HAND
// ==========================================================
def drawHand(
c: CanvasDraw,
x: Double,
y: Double,
side: Double
): Unit = {
c.fill(242, 244, 248)
c.stroke(90, 100, 110)
c.strokeWeight(1.3)
c.ellipse(x, y, 14, 18)
// Palm blue light
c.fill(15, 95, 175)
c.noStroke()
c.ellipse(
x,
y + 1,
8,
7
)
// Fingers
c.stroke(120, 130, 140)
c.strokeWeight(1.3)
for (i <- 0 until 3) {
val fy = y - 5.0 + i * 4.0
c.line(
x,
fy,
x + side * 7.0,
fy - 1.5
)
}
}
// ==========================================================
// PARTICLE UPDATE
// ==========================================================
def updateDust(): Unit = {
dust.foreach { p =>
p.x += p.vx
p.y += p.vy
p.vy += 0.025
p.life -= 0.025
p.size += 0.05
}
dust.filterInPlace(
_.life > 0.0
)
}
// ==========================================================
// CREATE DUST
// ==========================================================
def addDust(
x: Double,
y: Double
): Unit = {
for (_ <- 0 until 2) {
dust.append(
Dust(
x + ((math.random * 20.0) - 10.0),
y + math.random * 3.0,
((math.random * 1.4) - 0.7),
-(math.random * 0.9),
1.0,
2.0 + math.random * 3.0
)
)
}
}
// ==========================================================
// DRAW DUST
// ==========================================================
def drawDust(c: CanvasDraw): Unit = {
dust.foreach { p =>
val a =
alphaValue(
p.life * 120.0
)
c.fill(205, 215, 225, a)
c.noStroke()
c.ellipse(
p.x,
p.y,
p.size,
p.size
)
}
}
// ==========================================================
// FLOOR
// ==========================================================
def drawFloor(c: CanvasDraw): Unit = {
c.fill(11, 17, 28)
c.noStroke()
c.rect(
0,
0,
cwidth,
64
)
c.stroke(24, 86, 130, 160)
c.strokeWeight(1.5)
c.line(
0,
64,
cwidth,
64
)
// Horizontal futuristic lines
for (i <- 0 until 7) {
val y =
64.0 + i * 28.0
c.stroke(
20,
70,
110,
70
)
c.line(
0,
y,
cwidth,
y
)
}
// Perspective lines
val center = cwidth / 2.0
for (i <- -10 to 10) {
val bottomX =
center + i * 65.0
c.stroke(
20,
90,
135,
65
)
c.line(
center,
64,
bottomX,
0
)
}
}
// ==========================================================
// BACKGROUND
// ==========================================================
def drawBackground(c: CanvasDraw): Unit = {
c.background(
5,
9,
19
)
// Stars
for (i <- 0 until 70) {
val sx =
(i * 97) % cwidth
val sy =
90.0 +
((i * 59) % (cheight - 160))
val pulse =
1.0 +
math.sin(glowPhase + i * 0.37) * 0.5
val alpha =
alphaValue(
90.0 +
math.sin(glowPhase + i) * 45.0
)
c.fill(
180,
225,
255,
alpha
)
c.noStroke()
c.ellipse(
sx,
sy,
pulse * 2.0,
pulse * 2.0
)
}
// Horizon glow
c.fill(
20,
100,
155,
35
)
c.noStroke()
c.ellipse(
cwidth / 2.0,
90,
cwidth * 0.75,
120
)
drawFloor(c)
}
// ==========================================================
// ROBOT BODY
// ==========================================================
def drawRobot(c: CanvasDraw): Unit = {
val moving =
distance(
rx,
ry,
targetX,
targetY
) > 7.0
val walkAmount =
if (moving) {
math.sin(walkPhase)
} else {
0.0
}
// --------------------------------------------------------
// BODY BOB
// --------------------------------------------------------
val bob =
if (moving) {
math.abs(math.sin(walkPhase * 2.0)) * 2.8
} else {
math.sin(idlePhase) * 1.2
}
val baseX = rx
val baseY = ry + bob
// --------------------------------------------------------
// LARGE SHADOW
// --------------------------------------------------------
val shadowScale =
clamp(
1.0 -
math.abs(bob) * 0.05,
0.7,
1.0
)
c.fill(
0,
0,
0,
115
)
c.noStroke()
c.ellipse(
baseX,
ry - 60,
90 * shadowScale,
17
)
// ========================================================
// LEGS
// ========================================================
val leftLegSwing =
walkAmount * 0.38
val rightLegSwing =
-walkAmount * 0.38
// LEFT LEG
val leftHipX =
baseX - 18
val leftHipY =
baseY - 8
val leftKneeX =
leftHipX +
math.sin(leftLegSwing) * 18
val leftKneeY =
leftHipY - 32 +
math.abs(walkAmount) * 5
val leftFootX =
leftKneeX +
math.sin(leftLegSwing * 0.7) * 10
val leftFootY =
leftKneeY - 30
limb(
c,
leftHipX,
leftHipY,
leftKneeX,
leftKneeY,
11
)
joint(
c,
leftKneeX,
leftKneeY,
7
)
limb(
c,
leftKneeX,
leftKneeY,
leftFootX,
leftFootY,
10
)
drawFoot(
c,
leftFootX,
leftFootY,
-1
)
// RIGHT LEG
val rightHipX =
baseX + 18
val rightHipY =
baseY - 8
val rightKneeX =
rightHipX +
math.sin(rightLegSwing) * 18
val rightKneeY =
rightHipY - 32 +
math.abs(walkAmount) * 5
val rightFootX =
rightKneeX +
math.sin(rightLegSwing * 0.7) * 10
val rightFootY =
rightKneeY - 30
limb(
c,
rightHipX,
rightHipY,
rightKneeX,
rightKneeY,
11
)
joint(
c,
rightKneeX,
rightKneeY,
7
)
limb(
c,
rightKneeX,
rightKneeY,
rightFootX,
rightFootY,
10
)
drawFoot(
c,
rightFootX,
rightFootY,
1
)
// Knee armor
armor(
c,
leftKneeX,
leftKneeY,
13,
15
)
armor(
c,
rightKneeX,
rightKneeY,
13,
15
)
// ========================================================
// TORSO
// ========================================================
c.fill(
232,
235,
241
)
c.stroke(
95,
105,
118
)
c.strokeWeight(
2.0
)
c.ellipse(
baseX,
baseY + 18,
55,
64
)
// Torso side plates
c.fill(
192,
198,
208
)
c.noStroke()
c.ellipse(
baseX - 24,
baseY + 13,
11,
30
)
c.ellipse(
baseX + 24,
baseY + 13,
11,
30
)
// ========================================================
// CHEST ARMOR
// ========================================================
c.fill(
3,
40,
95
)
c.stroke(
20,
165,
240
)
c.strokeWeight(
2
)
c.ellipse(
baseX,
baseY + 25,
34,
31
)
c.fill(
35,
150,
240,
150
)
c.noStroke()
c.ellipse(
baseX - 7,
baseY + 31,
13,
7
)
// Reactor
val reactorGlow =
alphaValue(
160.0 +
math.sin(glowPhase * 2.0) *
70.0
)
c.fill(
0,
180,
255,
reactorGlow
)
c.ellipse(
baseX,
baseY + 25,
12,
12
)
c.fill(
205,
245,
255,
230
)
c.ellipse(
baseX,
baseY + 25,
5,
5
)
// ========================================================
// NECK
// ========================================================
c.fill(
170,
178,
190
)
c.stroke(
80,
90,
100
)
c.strokeWeight(
1.5
)
c.rect(
baseX - 9,
baseY + 45,
18,
10
)
// ========================================================
// ARMS
// ========================================================
val armWave =
walkAmount * 0.42
// LEFT ARM
val lShoulderX =
baseX - 27
val lShoulderY =
baseY + 35
val lElbowX =
lShoulderX -
16 -
armWave * 8
val lElbowY =
lShoulderY -
19
val lHandX =
lElbowX -
8 -
armWave * 5
val lHandY =
lElbowY -
25
armor(
c,
lShoulderX,
lShoulderY,
19,
20
)
limb(
c,
lShoulderX,
lShoulderY,
lElbowX,
lElbowY,
10
)
joint(
c,
lElbowX,
lElbowY,
6
)
limb(
c,
lElbowX,
lElbowY,
lHandX,
lHandY,
9
)
armor(
c,
lElbowX,
lElbowY,
13,
18
)
drawHand(
c,
lHandX,
lHandY,
-1
)
// RIGHT ARM
val rShoulderX =
baseX + 27
val rShoulderY =
baseY + 35
val rElbowX =
rShoulderX +
16 +
armWave * 8
val rElbowY =
rShoulderY -
19
val rHandX =
rElbowX +
8 +
armWave * 5
val rHandY =
rElbowY -
25
armor(
c,
rShoulderX,
rShoulderY,
19,
20
)
limb(
c,
rShoulderX,
rShoulderY,
rElbowX,
rElbowY,
10
)
joint(
c,
rElbowX,
rElbowY,
6
)
limb(
c,
rElbowX,
rElbowY,
rHandX,
rHandY,
9
)
armor(
c,
rElbowX,
rElbowY,
13,
18
)
drawHand(
c,
rHandX,
rHandY,
1
)
// Shoulder lights
glowDot(
c,
lShoulderX,
lShoulderY,
3.0,
glowPhase
)
glowDot(
c,
rShoulderX,
rShoulderY,
3.0,
glowPhase + 1.4
)
// ========================================================
// HEAD
// ========================================================
val headY =
baseY + 72
// Head shadow
c.fill(
0,
0,
0,
65
)
c.noStroke()
c.ellipse(
baseX + 2,
headY - 2,
48,
42
)
// Head
c.fill(
241,
243,
247
)
c.stroke(
95,
105,
116
)
c.strokeWeight(
2
)
c.ellipse(
baseX,
headY,
47,
41
)
// Blue helmet cap
c.fill(
4,
46,
105
)
c.stroke(
20,
165,
240
)
c.strokeWeight(
2
)
c.ellipse(
baseX,
headY + 18,
32,
10
)
// Cap reflection
c.fill(
100,
225,
255,
160
)
c.noStroke()
c.ellipse(
baseX - 6,
headY + 20,
12,
4
)
// ========================================================
// FACE TRACKING
// ========================================================
val lookX =
clamp(
(mouseX.toDouble - baseX) * 0.05,
-3.0,
3.0
)
val lookY =
clamp(
(mouseY.toDouble - headY) * 0.035,
-2.0,
2.0
)
// Eyes
val eyeOpen =
if (math.sin(blinkTimer) > 0.985) {
1.5
} else {
6.0
}
c.fill(
15,
20,
27
)
c.ellipse(
baseX - 9,
headY + 3,
7,
eyeOpen
)
c.ellipse(
baseX + 9,
headY + 3,
7,
eyeOpen
)
// Eye lights
c.fill(
80,
220,
255
)
c.ellipse(
baseX - 8 + lookX,
headY + 4 + lookY,
2.5,
2.5
)
c.ellipse(
baseX + 10 + lookX,
headY + 4 + lookY,
2.5,
2.5
)
// Mouth
c.stroke(
75,
85,
95
)
c.strokeWeight(
1.5
)
c.line(
baseX - 3,
headY - 8,
baseX + 3,
headY - 8
)
// ========================================================
// SIDE HEAD PANELS
// ========================================================
c.fill(
205,
210,
218
)
c.stroke(
90,
100,
110
)
c.strokeWeight(
1.2
)
c.ellipse(
baseX - 23,
headY + 1,
7,
12
)
c.ellipse(
baseX + 23,
headY + 1,
7,
12
)
// ========================================================
// ANTENNA
// ========================================================
c.stroke(
140,
150,
165
)
c.strokeWeight(
2
)
c.line(
baseX,
headY + 24,
baseX,
headY + 31
)
glowDot(
c,
baseX,
headY + 34,
2.7,
glowPhase * 2.0
)
// ========================================================
// CHEST SIDE LIGHTS
// ========================================================
glowDot(
c,
baseX - 18,
baseY + 17,
2,
glowPhase + 0.7
)
glowDot(
c,
baseX + 18,
baseY + 17,
2,
glowPhase + 2.0
)
}
// ==========================================================
// HUD
// ==========================================================
def drawHUD(c: CanvasDraw): Unit = {
// Top title
c.fill(
0,
15,
35,
190
)
c.stroke(
0,
170,
240,
150
)
c.strokeWeight(
1.5
)
c.rect(
18,
cheight - 60,
290,
38
)
c.fill(
125,
230,
255
)
c.noStroke()
c.text(
"TITAN X-1 // ACTIVE",
30,
cheight - 37
)
// Bottom instructions
c.fill(
175,
210,
235
)
c.text(
"MOVE MOUSE ? ROBOT FOLLOWS",
28,
cheight - 85
)
// Speed
val robotSpeed =
math.abs(speedX) * 10.0
c.fill(
70,
200,
255
)
c.text(
"MOTION: " +
robotSpeed.toInt.toString +
"%",
28,
cheight - 108
)
// System lights
c.fill(
20,
35,
52
)
c.stroke(
0,
150,
220,
140
)
c.strokeWeight(
1
)
c.rect(
cwidth - 175,
cheight - 60,
145,
38
)
val systemAlpha =
alphaValue(
120.0 +
math.sin(glowPhase) * 80.0
)
c.fill(
0,
220,
255,
systemAlpha
)
c.ellipse(
cwidth - 155,
cheight - 41,
7,
7
)
c.fill(
175,
225,
240
)
c.text(
"SYSTEM ONLINE",
cwidth - 140,
cheight - 38
)
}
// ==========================================================
// TARGET RETICLE
// ==========================================================
def drawTarget(c: CanvasDraw): Unit = {
val pulse =
22.0 +
math.sin(glowPhase * 2.0) * 5.0
c.noFill()
c.stroke(
60,
205,
255,
135
)
c.strokeWeight(
1.5
)
c.ellipse(
targetX,
targetY,
pulse,
pulse
)
c.line(
targetX - 14,
targetY,
targetX - 5,
targetY
)
c.line(
targetX + 5,
targetY,
targetX + 14,
targetY
)
c.line(
targetX,
targetY - 14,
targetX,
targetY - 5
)
c.line(
targetX,
targetY + 5,
targetX,
targetY + 14
)
}
// ==========================================================
// SETUP
// ==========================================================
def setup(surface: CanvasDraw): Unit = {
import surface._
background(
5,
9,
19
)
}
// ==========================================================
// MAIN LOOP
// ==========================================================
def drawLoop(surface: CanvasDraw): Unit = {
import surface._
// --------------------------------------------------------
// BACKGROUND
// --------------------------------------------------------
drawBackground(surface)
// --------------------------------------------------------
// MOUSE TARGET
// --------------------------------------------------------
targetX =
clamp(
mouseX.toDouble,
80.0,
cwidth.toDouble - 80.0
)
targetY =
clamp(
mouseY.toDouble,
100.0,
cheight.toDouble - 130.0
)
// --------------------------------------------------------
// SMOOTH MOTION
// --------------------------------------------------------
val dx =
targetX - rx
val dy =
targetY - ry
rx += dx * 0.075
ry += dy * 0.075
// --------------------------------------------------------
// SPEED
// --------------------------------------------------------
speedX =
rx - lastX
lastX =
rx
val moving =
math.abs(dx) > 7.0 ||
math.abs(dy) > 7.0
// --------------------------------------------------------
// WALK ANIMATION
// --------------------------------------------------------
if (moving) {
walkPhase += 0.22
dustTimer += 1.0
if (dustTimer > 5.0) {
addDust(
rx - speedX * 2.0,
ry - 55
)
dustTimer = 0.0
}
} else {
idlePhase += 0.05
walkPhase *= 0.96
}
// --------------------------------------------------------
// GLOBAL ANIMATION
// --------------------------------------------------------
glowPhase += 0.08
blinkTimer += 0.025
// --------------------------------------------------------
// PARTICLES
// --------------------------------------------------------
updateDust()
drawDust(surface)
// --------------------------------------------------------
// ROBOT
// --------------------------------------------------------
drawRobot(surface)
// --------------------------------------------------------
// TARGET
// --------------------------------------------------------
drawTarget(surface)
// --------------------------------------------------------
// HUD
// --------------------------------------------------------
drawHUD(surface)
}
}
// ============================================================
// START
// ============================================================
val sketch = new Sketch
canvasSketch(sketch)