parent
0d97878596
commit
f6f690837e
13 changed files with 491 additions and 383 deletions
@ -0,0 +1,131 @@ |
||||
// Scare mechanic, single particle.
|
||||
import Rx, { Observable } from 'rxjs'; |
||||
import AnimationBase from './animationBase'; |
||||
import DOM from './dom'; |
||||
import Store from './store'; |
||||
|
||||
const evtScare = (scareX, scareY) => new CustomEvent("scare", { detail: { scareX, scareY } }); |
||||
|
||||
const particleDiv = document.createElement('div'); |
||||
particleDiv.className = 'anim2-particle'; |
||||
|
||||
function checkScare([evt, store]) { |
||||
const state = store.get(); |
||||
|
||||
const { evtX, evtY } = DOM.getEventOffsetCoords(evt, DOM.containerBounds); |
||||
const diffX = Math.abs(state.x - evtX); |
||||
const diffY = Math.abs(state.y - evtY); |
||||
|
||||
if (evt.target === particleDiv) { |
||||
DOM.container.dispatchEvent(evtScare(evtX, evtY)); |
||||
} |
||||
}; |
||||
|
||||
function move(acc, i) { |
||||
let { x, y, dx, dy } = acc; |
||||
|
||||
const east = DOM.containerBounds.width - particleDiv.offsetWidth; |
||||
const south = DOM.containerBounds.height - particleDiv.offsetHeight; |
||||
|
||||
x += dx; |
||||
y += dy; |
||||
|
||||
if (x < 0) { |
||||
x = Math.abs(x); |
||||
dx = -dx; |
||||
} |
||||
|
||||
if (x > east) { |
||||
x = Math.round(2 * east - x); |
||||
dx = -dx; |
||||
} |
||||
|
||||
if (y < 0) { |
||||
y = Math.abs(y); |
||||
dy = -dy; |
||||
} |
||||
|
||||
if (y > south) { |
||||
y = Math.round(2 * south - y); |
||||
dy = -dy; |
||||
} |
||||
|
||||
return { x, y, dx, dy }; |
||||
}; |
||||
|
||||
function flee([evt, store]) { |
||||
const initialState = store.get(); |
||||
const fleeRadius = 200; |
||||
const { scareX, scareY } = evt.detail; |
||||
const fps$ = Rx.Observable.interval(1000 / 32); |
||||
|
||||
const frames$ = fps$ |
||||
.scan(move, initialState) |
||||
.takeWhile(state => { |
||||
const xDanger = Math.abs(initialState.x - state.x) < fleeRadius; |
||||
const yDanger = Math.abs(initialState.y - state.y) < fleeRadius; |
||||
|
||||
return xDanger && yDanger; |
||||
}) |
||||
|
||||
frames$.last().subscribe(finalState => { |
||||
store.set(finalState); |
||||
store.set(randomMoveVector()); |
||||
}); |
||||
|
||||
frames$.subscribe(state => { |
||||
particleDiv.style.left = `${state.x}px`; |
||||
particleDiv.style.top = `${state.y}px`; |
||||
}); |
||||
}; |
||||
|
||||
function randomMoveVector() { |
||||
const speed = 10; |
||||
let dx = Math.round(Math.random() * speed); |
||||
let dy = Math.pow(Math.pow(speed, 2) - Math.pow(dx, 2), 0.5); |
||||
|
||||
const negX = Math.random() < 0.5 ? -1 : 1; |
||||
const negY = Math.random() < 0.5 ? -1 : 1; |
||||
|
||||
dx *= negX; |
||||
dy *= negY; |
||||
|
||||
return { dx, dy }; |
||||
} |
||||
|
||||
function reset() { |
||||
if (particleDiv.parentNode) { |
||||
DOM.container.removeChild(particleDiv); |
||||
} |
||||
|
||||
const { dx, dy } = randomMoveVector(); |
||||
const store = new Store({ x: 0, y: 0, dx, dy }); |
||||
const state = store.get(); |
||||
|
||||
particleDiv.style.top = `${state.y}px`; |
||||
particleDiv.style.left = `${state.x}px`; |
||||
|
||||
DOM.container.appendChild(particleDiv); |
||||
|
||||
return store; |
||||
}; |
||||
|
||||
function init() { |
||||
const store = reset(); |
||||
|
||||
const click$ = Rx.Observable |
||||
.fromEvent(DOM.container, 'click') |
||||
// .do(DOM.calcBounds)
|
||||
.do(DOM.highlight) |
||||
.map(evt => [evt, store]) |
||||
.subscribe(checkScare); |
||||
|
||||
Rx.Observable |
||||
.fromEvent(DOM.container, 'scare') |
||||
.map(evt => [evt, store]) |
||||
.subscribe(flee); |
||||
}; |
||||
|
||||
const Animation2 = Object.assign({}, AnimationBase, { init, reset }); |
||||
|
||||
export default Animation2; |
@ -1,131 +1,87 @@ |
||||
// Scare mechanic, single particle.
|
||||
import Rx, { Observable } from 'rxjs'; |
||||
import AnimationBase from './animationBase'; |
||||
import DOM from './dom'; |
||||
import Particle from './particle'; |
||||
import Store from './store'; |
||||
import Controls from './controls'; |
||||
import { CONTROLS } from './enums'; |
||||
|
||||
const evtScare = (scareX, scareY) => new CustomEvent("scare", { detail: { scareX, scareY } }); |
||||
function Animation2() { |
||||
this.options = { |
||||
animating: false, |
||||
count: 1, |
||||
maxCount: 3000, |
||||
speed: 4 |
||||
}; |
||||
|
||||
const particleDiv = document.createElement('div'); |
||||
particleDiv.className = 'anim2-particle'; |
||||
this.container = document.getElementById('animation2'); |
||||
this.bounds = this.container.getBoundingClientRect(); |
||||
|
||||
function checkScare([evt, store]) { |
||||
const state = store.get(); |
||||
this.particles = []; |
||||
|
||||
const { evtX, evtY } = DOM.getEventOffsetCoords(evt, DOM.containerBounds); |
||||
const diffX = Math.abs(state.x - evtX); |
||||
const diffY = Math.abs(state.y - evtY); |
||||
const controls = new Controls( |
||||
document.getElementById('controls2'), |
||||
this.options |
||||
); |
||||
|
||||
if (evt.target === particleDiv) { |
||||
DOM.container.dispatchEvent(evtScare(evtX, evtY)); |
||||
} |
||||
}; |
||||
controls.mount().subscribe(this.subscriber.bind(this)); |
||||
|
||||
function move(acc, i) { |
||||
let { x, y, dx, dy } = acc; |
||||
this.updateAnimating(this.options.animating); |
||||
this.updateCount(this.options.count); |
||||
|
||||
const east = DOM.containerBounds.width - particleDiv.offsetWidth; |
||||
const south = DOM.containerBounds.height - particleDiv.offsetHeight; |
||||
// TODO X dimension modified by core UI
|
||||
// TODO "shaky" / "stuck" bug
|
||||
|
||||
x += dx; |
||||
y += dy; |
||||
|
||||
if (x < 0) { |
||||
x = Math.abs(x); |
||||
dx = -dx; |
||||
} |
||||
// TODO ANIM3 Show vision grid (including touches!)
|
||||
// TODO ANIM3 regen vision grid is option updated
|
||||
// TODO ANIM3 cleanup vision grid
|
||||
// TODO ANIM3 Pallet avoidance
|
||||
|
||||
if (x > east) { |
||||
x = Math.round(2 * east - x); |
||||
dx = -dx; |
||||
} |
||||
|
||||
if (y < 0) { |
||||
y = Math.abs(y); |
||||
dy = -dy; |
||||
} |
||||
|
||||
if (y > south) { |
||||
y = Math.round(2 * south - y); |
||||
dy = -dy; |
||||
} |
||||
|
||||
return { x, y, dx, dy }; |
||||
// TODO ANIM4 Flocking
|
||||
}; |
||||
|
||||
function flee([evt, store]) { |
||||
const initialState = store.get(); |
||||
const fleeRadius = 200; |
||||
const { scareX, scareY } = evt.detail; |
||||
const fps$ = Rx.Observable.interval(1000 / 32); |
||||
|
||||
const frames$ = fps$ |
||||
.scan(move, initialState) |
||||
.takeWhile(state => { |
||||
const xDanger = Math.abs(initialState.x - state.x) < fleeRadius; |
||||
const yDanger = Math.abs(initialState.y - state.y) < fleeRadius; |
||||
|
||||
return xDanger && yDanger; |
||||
}) |
||||
|
||||
frames$.last().subscribe(finalState => { |
||||
store.set(finalState); |
||||
store.set(randomMoveVector()); |
||||
}); |
||||
|
||||
frames$.subscribe(state => { |
||||
particleDiv.style.left = `${state.x}px`; |
||||
particleDiv.style.top = `${state.y}px`; |
||||
}); |
||||
}; |
||||
Animation2.prototype.subscriber = function({ key, value }) { |
||||
switch(key) { |
||||
case CONTROLS.ANIMATING: this.updateAnimating(value); break; |
||||
case CONTROLS.COUNT: this.updateCount(value); break; |
||||
case CONTROLS.SPEED: this.updateSpeed(value); break; |
||||
} |
||||
} |
||||
|
||||
function randomMoveVector() { |
||||
const speed = 10; |
||||
let dx = Math.round(Math.random() * speed); |
||||
let dy = Math.pow(Math.pow(speed, 2) - Math.pow(dx, 2), 0.5); |
||||
Animation2.prototype.nextFrame = function() { |
||||
this.particles.forEach(p => p.nextFrame()); |
||||
} |
||||
|
||||
const negX = Math.random() < 0.5 ? -1 : 1; |
||||
const negY = Math.random() < 0.5 ? -1 : 1; |
||||
Animation2.prototype.updateAnimating = function(isAnimating) { |
||||
this.options.animating = isAnimating; |
||||
|
||||
dx *= negX; |
||||
dy *= negY; |
||||
if (isAnimating) { |
||||
const fps$ = Rx.Observable.interval(1000 / 32) |
||||
.takeWhile(_ => this.options.animating); |
||||
|
||||
return { dx, dy }; |
||||
fps$.subscribe(this.nextFrame.bind(this)); |
||||
} |
||||
} |
||||
|
||||
function reset() { |
||||
if (particleDiv.parentNode) { |
||||
DOM.container.removeChild(particleDiv); |
||||
Animation2.prototype.updateCount = function(count) { |
||||
while (this.particles.length >= count) { |
||||
const p = this.particles.pop(); |
||||
this.container.removeChild(p.node); |
||||
} |
||||
|
||||
const { dx, dy } = randomMoveVector(); |
||||
const store = new Store({ x: 0, y: 0, dx, dy }); |
||||
const state = store.get(); |
||||
|
||||
particleDiv.style.top = `${state.y}px`; |
||||
particleDiv.style.left = `${state.x}px`; |
||||
|
||||
DOM.container.appendChild(particleDiv); |
||||
|
||||
return store; |
||||
}; |
||||
|
||||
function init() { |
||||
const store = reset(); |
||||
|
||||
const click$ = Rx.Observable |
||||
.fromEvent(DOM.container, 'click') |
||||
// .do(DOM.calcBounds)
|
||||
.do(DOM.highlight) |
||||
.map(evt => [evt, store]) |
||||
.subscribe(checkScare); |
||||
while (this.particles.length < count) { |
||||
const p = new Particle(this.container, this.bounds, this.options); |
||||
this.particles.push(p); |
||||
} |
||||
} |
||||
|
||||
Rx.Observable |
||||
.fromEvent(DOM.container, 'scare') |
||||
.map(evt => [evt, store]) |
||||
.subscribe(flee); |
||||
}; |
||||
Animation2.prototype.updateMovementCircle = function(showMovementCircle) { |
||||
this.options.showMovementCircle = showMovementCircle; |
||||
this.movementCircleCtrl.querySelector('[type=checkbox]').checked = showMovementCircle; |
||||
this.particles.forEach(p => p.updateOptions({ showMovementCircle })); |
||||
} |
||||
|
||||
const Animation2 = Object.assign({}, AnimationBase, { init, reset }); |
||||
Animation2.prototype.updateSpeed = function(speed) { |
||||
this.options.speed = speed; |
||||
this.particles.forEach(p => p.updateOptions({ speed })); |
||||
} |
||||
|
||||
export default Animation2; |
||||
|
@ -1,110 +0,0 @@ |
||||
// Join mechanic, multiple particles
|
||||
// ????? Chase, maybe?
|
||||
// ???? Occupied?
|
||||
|
||||
// called each frame update
|
||||
// find if going to hit a wall
|
||||
// find if palette nearby
|
||||
|
||||
// const grid = {};
|
||||
// for (let x = 0; x <= 600; x += 5) {
|
||||
// grid[x] = {};
|
||||
// for (let y = 0; y <= 600; y += 5) {
|
||||
// grid[x][y] = { type: null };
|
||||
//
|
||||
// if (x === 0 || y === 0 || x === 600 || y === 600) {
|
||||
// grid[x][y] = { type: 'wall' };
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
import Rx, { Observable } from 'rxjs'; |
||||
import AnimationBase from './animationBase'; |
||||
import DOM from './dom'; |
||||
import Store from './store'; |
||||
|
||||
const evtScare = (detail) => new CustomEvent('scare', { detail }); |
||||
|
||||
const [particles, state] = (new Array(5)).fill(null).reduce((acc, v, i) => { |
||||
const div = document.createElement('div'); |
||||
div.className = 'anim3-particle'; |
||||
div.innerHTML = '' |
||||
|
||||
const x = 0; |
||||
const y = i * 25; |
||||
|
||||
div.style.left = 0 |
||||
div.style.top = `${y}px`; |
||||
|
||||
acc[0].push(div); |
||||
acc[1].push({ x, y }); |
||||
|
||||
return acc; |
||||
}, [[], []]); |
||||
|
||||
const palettes = (new Array(1)).fill(null).reduce((acc, v, i) => { |
||||
const div = document.createElement('div'); |
||||
div.className = 'palette'; |
||||
div.style.left = '200px'; |
||||
div.style.top = '200px'; |
||||
|
||||
acc.push(div); |
||||
|
||||
return acc; |
||||
}, []); |
||||
|
||||
function scare(evt) { |
||||
const bounds = DOM.container.getBoundingClientRect(); |
||||
const { evtX: x, evtY: y } = DOM.getEventOffsetCoords(evt, bounds); |
||||
const scareRadius = 50; |
||||
|
||||
state.forEach((coord, i) => { |
||||
const diffX = Math.abs(coord.x - x + 10); |
||||
const diffY = Math.abs(coord.y - y + 10); |
||||
|
||||
if (diffX < 100 && diffY < 100) { |
||||
coord.lastScare = { x, y } // TODO set state with last scare, then judge per frame based on that number to avoid jump
|
||||
DOM.container.dispatchEvent(evtScare({ x, y, i })); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
function reset() { |
||||
while (DOM.container.childNodes.length) { |
||||
DOM.container.removeChild(DOM.container.firstChild); |
||||
} |
||||
|
||||
particles.forEach((div) => { |
||||
div.innerHTML = ''; |
||||
DOM.container.appendChild(div) |
||||
}); |
||||
|
||||
console.warn(palettes) |
||||
|
||||
palettes.forEach((div) => { |
||||
DOM.container.appendChild(div) |
||||
}); |
||||
}; |
||||
|
||||
function init() { |
||||
reset(); |
||||
|
||||
const click$ = Rx.Observable.fromEvent(DOM.container, 'click'); |
||||
const scare$ = Rx.Observable.fromEvent(DOM.container, 'scare').auditTime(100); |
||||
|
||||
scare$.subscribe(evt => { |
||||
particles[evt.detail.i].innerHTML = 'S' |
||||
DOM.addClass(particles[evt.detail.i], 'scared'); |
||||
const p = particles[evt.detail.i]; |
||||
setTimeout(() => { |
||||
p.innerHTML = ''; |
||||
DOM.removeClass(p, 'scared'); |
||||
}, 1000); |
||||
}); |
||||
|
||||
click$.subscribe(scare); |
||||
}; |
||||
|
||||
const Animation3 = Object.assign({}, AnimationBase, { init, reset }); |
||||
|
||||
export default Animation3; |
Loading…
Reference in new issue