{/* <~~ div is never rendered, it is used as a container for
66 | components that don't need to be a child of Scene */}
67 |
68 | {/* i must b crzy...
69 | using components for application logic (◑ ‿ ◐) */}
70 |
71 |
72 |
73 |
{
76 | this.scene = scene;
77 | this.props.refScene(scene);
78 | }}
79 | camera="maincamera">
80 |
81 |
82 |
83 |
84 | {flakes =>
85 |
86 |
87 | {flakes.map((flake,index) =>
88 | {
92 | if (!flake.explode) {
93 | playRandomPfSound();
94 | explodeFlake(index);
95 | }
96 | }}
97 | materialIndex={flake.materialIndex}
98 | spinningTick={flake.spinningTick}
99 | quaternion={flake.quaternionXY}
100 | scale={flake.scale}
101 | position={
102 | new Vector3(
103 | tween(flake.tick, flake.driftKeyframes),
104 | tween(flake.tick, boundsKeyframes),
105 | 0
106 | )}
107 | />)}
108 |
109 |
110 | }
111 |
112 |
113 |
114 | )
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/appStateProvider.js:
--------------------------------------------------------------------------------
1 | import stateful from 'react-stateful-stream';
2 | import u from 'updeep';
3 | import compose from 'lodash.compose';
4 |
5 | const immutable = u({});
6 |
7 | const flakeHasId = id => flake => flake.id === id;
8 | const concat = newItem => items => items.concat(newItem);
9 | const incrementByOne = x => x + 1;
10 |
11 | // when the flake is exploding, spinning slows down
12 | const spinningTickIncrementer = ({explode, increment, explodingTick}) =>
13 | x => x + (explode ? increment * ((60-explodingTick) / 60) : increment);
14 |
15 | const tickIncrementer = ({explode, increment}) =>
16 | x => x + increment;
17 |
18 | const defaultInitialState = immutable({
19 | flakes: [],
20 | droppedCount: 0,
21 | gameIsOver: false,
22 | score: 0
23 | });
24 |
25 | // These are our application state reducers...
26 | // note that the signature of the `edit` function is simply:
27 | //
28 | // {application state} => {transformed application state}
29 | //
30 | const selectEdit = edit => ({
31 | addFlake: newFlake => {
32 | edit(u.if(
33 | ({flakes}) => flakes.length < 15
34 | , {
35 | flakes: concat(newFlake),
36 | droppedCount: incrementByOne
37 | }
38 | ))
39 | },
40 |
41 | removeFlake: flakeId => edit(u({
42 | flakes: u.reject(flakeHasId(flakeId))
43 | })),
44 |
45 | explodeFlake: index => edit(u({
46 | flakes: {
47 | [index]: { explode: true }
48 | }
49 | })),
50 |
51 | tickFlakes: () => edit(u({
52 | flakes: compose(
53 | u.reject(flake =>
54 | flake.tick > 100 ||
55 | flake.explodingTick > 60)
56 | , u.map(
57 | flake => u({
58 | tick: tickIncrementer(flake),
59 | spinningTick: spinningTickIncrementer(flake),
60 | explodingTick: u.if(flake.explode, incrementByOne),
61 | }, flake)
62 | )
63 | )
64 | })),
65 |
66 | playAgain: () => edit(u({
67 | gameIsOver: false,
68 | score: 0,
69 | flakes: [],
70 | droppedCount: 0
71 | })),
72 |
73 | gameOver: () => edit(u({
74 | gameIsOver: true
75 | })),
76 |
77 | addToScore: amount => edit(u({
78 | score: x => x + ~~amount
79 | })),
80 | });
81 |
82 | const options = { provider: true };
83 |
84 | export default initialState => DecoratedComponent => initialState ?
85 | stateful(initialState, selectEdit, options)(DecoratedComponent) :
86 | stateful(defaultInitialState, selectEdit, options)(DecoratedComponent);
87 |
--------------------------------------------------------------------------------
/src/createFlake.js:
--------------------------------------------------------------------------------
1 | import { flakeMaterials } from './materials';
2 | import { Quaternion, Euler } from 'three';
3 |
4 | const quaternionFromEuler = (...args) =>
5 | new Quaternion().setFromEuler(
6 | new Euler(...args)
7 | );
8 |
9 | export default function createFlake(id, x) {
10 | const quaternionXY = quaternionFromEuler(
11 | Math.random()*0.5 - 0.25, Math.random()*0.5 - 0.25, 0, 'XYZ'
12 | );
13 |
14 | return ({
15 | id,
16 | tick: 0,
17 | spinningTick: 0,
18 | explodingTick: 0,
19 | materialIndex: ~~(flakeMaterials.length * Math.random()),
20 | scale: 2 + (Math.random() * 3),
21 | // rotationSpeed: Math.random() * 90 - 20,
22 | quaternionXY,
23 | driftKeyframes: {
24 | 0: x,
25 | 100: x + ~~(Math.random() * 40) - 15 },
26 | increment: .1 + Math.random()*0.2,
27 | });
28 | }
29 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import 'es6-shim';
2 | import './react-three-ejecta';
3 | import React from 'react';
4 | import ReactTHREE from 'react-three';
5 | import Game from './Game';
6 | import appStateProvider from './appStateProvider';
7 |
8 | const canvas = document.getElementById('canvas');
9 |
10 | const AppComponent = () =>