Sling
Started off by simulating a rope, ended up attaching a "stone" to it to make a classical sling.
Stay still and click somewhere in the left end to pick up a stone, then spin to throw it at the target.
/* 2019.19 - "sling" */
let rope = Array(10).fill().map((_, i) => ({x: 0, y: i, vx: 0, vy: 0}))
let stones = []
let primed = false
let gravity = 1
let limitX = 230
let target = {x: 500, y: 200, d: 50, w: 0, bull: false, high: false}
let targetC = 0
let stoneC = 0
let bests = []
let version = 3
function setup() {
createCanvas(900, 600);
cursor("grab")
bests = getItem("4aVBVWfeC-bests") || bests
if (!bests[0] || bests[0] < version) {
bests = [version]
}
}
function draw() {
let mist = color(154, 245, 245)
let grass = color(59, 160, 50)
background(mist);
noStroke()
for (let i = 0; i < 5; i ++) {
fill(lerpColor(mist, grass, (i + 1) / 5))
if (i == 0 && bests[targetC + 1]) {
textSize(200)
textStyle(BOLD)
textAlign(LEFT, BOTTOM)
text(bests[targetC + 1], width / 2, 320 + pow(i / 5, 1) * 150 + 100 * noise(5 * 0.2 / 2, 0) + 60)
}
if (i == 1) {
textSize(200)
textStyle(BOLD)
textAlign(bests[targetC + 1] ? RIGHT : CENTER, BOTTOM)
text(stoneC, width / 2, 320 + pow(i / 5, 1.2) * 150 + 100 * noise(5 * 0.2 / 2, i * 3) + 60)
}
beginShape()
vertex(0, height)
vertex(0, 220 + sqrt(i / 5) * 100 + 100 * noise(0, i * 3))
for (let j = 0; j <= 5; j ++) {
splineVertex(j * width / 5, 320 + pow(i / 5, 1) * 150 + 100 * noise(j * 0.2, i * 3))
}
vertex(width, 320 + pow(i / 5, 1.5) * 150 + 100 * noise(5 * 0.2, i * 3))
vertex(width, height)
endShape()
}
let pin = {x: (constrain(mouseX, 0, width)), y: constrain(mouseY, 0, height)}
if (pin.x > limitX) {
pin.x = log(pin.x - limitX) * 2 + limitX
}
noStroke()
if (pin.x > limitX) {
fill(250, 200, 100, constrain(mouseX - limitX, 0, 160))
beginShape()
vertex(0, 0)
vertex(limitX, 0)
splineVertex(pin.x, pin.y)
vertex(limitX, height)
vertex(0, height)
endShape()
}
for (let i = 0; i < rope.length; i ++) {
rope[i].vx *= 0.9
rope[i].vy *= 0.9
for (let s of [-1, 1]) {
let other = rope[i + s] || pin
let dx = other.x - rope[i].x
let dy = other.y - rope[i].y
let d = max(mag(dx, dy), 1)
let l = (i + s / 2 == (rope.length - 1) / 2 ? 21 : 3)
let x = (d - l) * 0.58
rope[i].vx += x * dx/d
rope[i].vy += x * dy/d
}
}
for (let i = 0; i < rope.length; i ++) {
rope[i].x += rope[i].vx
rope[i].y += rope[i].vy
rope[i].vy += gravity
}
noFill()
stroke(0)
beginShape()
vertex(pin.x, pin.y)
for (let i = 1; i < rope.length; i ++) {
splineVertex(rope[i - 1].x, rope[i - 1].y, rope[i].x, rope[i].y)
// circle(rope[i].x, rope[i].y, 2)
}
vertex(pin.x, pin.y)
endShape()
let si = rope.length / 2 - 1
let s = {
x: (rope[si].x + rope[si + 1].x) / 2,
y: (rope[si].y + rope[si + 1].y) / 2,
vx: (rope[si].vx + rope[si + 1].vx) / 2,
vy: (rope[si].vy + rope[si + 1].vy) / 2,
}
if (mouseIsPressed && (primed || mag(s.vy, s.vx) < 6)) {
primed = true
cursor("grabbing")
} else if (primed) {
stones.push(s)
if (s.vx > 6 || mag(s.vy, s.vx) > 12 || s.x > 200) {
stoneC ++
}
primed = false
cursor("grab")
}
fill(0)
noStroke()
if (primed) {
circle(s.x, s.y, 13)
}
for (let i = 0; i < stones.length; i ++) {
let steps = min(mag(stones[i].vx, stones[i].vy) / 10, 7)
for (let s = 0; s < steps; s ++) {
stones[i].x += stones[i].vx/steps
stones[i].y += stones[i].vy/steps
let d = dist(stones[i].x, stones[i].y, target.x, target.y)
if (d < target.d / 2 + 5) {
target.w = 1
if (stones[i].vy > 17.5) {
target.high = true
}
if (d < target.d / 6) {
target.bull = true
}
}
}
stones[i].vx *= 0.99
stones[i].vy *= 0.99
stones[i].vy += gravity
fill(0)
circle(stones[i].x, stones[i].y, 13)
let tx = stones[i].x
let ty = stones[i].y
if (stones[i].y < 0) {
ty = log(1-stones[i].y) * 2
}
if (stones[i].x < 0) {
tx = log(1-stones[i].x) * 2
}
if (stones[i].x > width) {
tx = width - log(stones[i].x - width + 1) * 2
}
if (stones[i].y > height) {
ty = height - log(stones[i].y - height + 1) * 2
fill(0, map(stones[i].y, height, height * 2, 100, 0))
stones[i].vx *= 0.7
}
if (tx != stones[i].x || ty != stones[i].y) {
push()
translate(tx, ty)
rotate(atan2(ty - stones[i].y, tx - stones[i].x))
triangle(0, 0, 5, 5, 5, -5)
pop()
}
}
stroke(0)
fill(250, 0, 0)
if (target.w) {
target.w ++
textStyle(NORMAL)
textSize(10)
textAlign(CENTER, BOTTOM)
let score = `${(targetC + 1)}/${stoneC}`
if (target.high) {
score = `high shot!\n${score}`
}
if (target.bull) {
score = `bullseye!\n${score}`
}
if ((targetC + 1) == stoneC) {
score = `${score}\nperfect!!`
} else if ((targetC + 1) * 2 >= stoneC) {
score = `${score}\ngood!`
} else if ((targetC + 1) * 4 >= stoneC) {
score = `${score}\nnice!`
}
if (bests[targetC + 1] > stoneC) {
score = `${score}\nnew best!`
}
text(score, target.x, target.y - target.d / 2 - target.w / 3)
if (target.w > 50) {
target = {
d: 50 * pow(0.97, targetC + random()),
x: random(600, 900) - target.d,
y: random(target.d, 400-target.d),
w: 0,
bull: false,
high: false
}
targetC ++
bests[targetC] = bests[targetC] < stoneC ? bests[targetC] : stoneC
storeItem("4aVBVWfeC-bests", bests)
}
fill(0, 255, 0)
}
let bounce = sin(frameCount / 7) / 3
circle(target.x, target.y + bounce, target.d)
push()
fill(255)
circle(target.x, target.y + bounce, target.d / 3 * 2)
pop()
circle(target.x, target.y + bounce, target.d / 3)
textStyle(BOLD)
noStroke()
fill(255)
textSize(floor(target.d / 3) - 1)
textAlign(CENTER, CENTER)
text(targetC + 1, target.x, target.y)
let j = 0;
for (let i = 0; i < stones.length; i ++) {
if ((stones[i].y < height * 2 || stones[i].vy < 0)) {
stones[j] = stones[i]
j ++
}
}
stones.splice(j, stones.length - j)
}(Originally seen at https://editor.p5js.org/bojidar-bg/sketches/4aVBVWfeC)
Browse more articles?
← Butterfly/bat Experiments tagged p5 (57/85) Match sets →
← Color Match Experiments tagged game (5/11) Match sets →
← Butterfly/bat Experiments on this site (57/85) Match sets →