// To-do // 1. add jelly effect for the balls 2. add highlight and animation for the walls 3. solve tunneling problem for edge cases ////// Start of Instrument Code class Instrument extends Chugraph { fun void setPitch(float pitch){} fun void play(float pitch){} } class Beep extends Instrument { Envelope e; JCRev r; Gain bus; Gain g; float pitch; SqrOsc sqr => bus; SawOsc saw1 => bus; SawOsc saw2 => bus; SinOsc sin1 => bus; SinOsc sin2 => bus; SinOsc sin3 => bus; SinOsc sin4 => bus; 1.5 => saw1.gain; 0.2 => saw2.gain; 0.5 => sin2.gain; 0.25 => sin3.gain; 0.1 => sin4.gain; 0.02 => r.mix; 0.05 => g.gain; bus => e => r => g => dac; fun void setPitch(float pitch) { pitch => sqr.freq; pitch => saw1.freq; pitch * 2 => saw2.freq; pitch => sin1.freq; pitch * 2 => sin2.freq; pitch * 3 => sin3.freq; pitch * 4 => sin4.freq; } fun void play(float pitch) { setPitch(pitch); e.keyOn(); 200::ms => now; e.keyOff(); } } class Bop extends Instrument { TubeBell b; Envelope e; JCRev r; Gain g; 0.02 => r.mix; 0.1 => g.gain; fun void setPitch(float pitch){ pitch => b.freq; } b => e => r => g => dac; fun void play(float pitch) { e.keyOn(); 200::ms => now; e.keyOff(); } } // thanks Tristan class Kick extends Instrument { Noise n => LPF f => ADSR e => Gain g => dac; 110 => f.freq; 40 => f.gain; e.set(5::ms, 50::ms, 0.1, 100::ms); fun void setPitch(float pitch){ pitch => f.freq; } fun void play(float pitch) { setPitch(pitch); e.keyOn(); 50::ms => now; e.keyOff(); e.releaseTime() => now; } } // thanks Tristan class Snare extends Instrument { Noise n => BPF f => ADSR e => Gain g => dac; 600 => f.freq; 15. => f.Q; 15 => f.gain; 0.5 => g.gain; e.set(5::ms, 50::ms, 0.1, 50::ms); fun void play(float pitch) { pitch => f.freq; e.keyOn(); 50::ms => now; e.keyOff(); e.releaseTime() => now; } } ////// End of Instrument code ////// Start of Sequencer code [ @(0, 0, 0), @(155, 95, 224), @(22, 164, 216), @(96, 219, 232), @(139, 211, 70), @(239, 223, 72), @(249, 165, 44), @(214, 78, 18) ] @=> vec3 palette[]; [ 0.0, Std.mtof(57), Std.mtof(61), Std.mtof(64), Std.mtof(69), Std.mtof(73), Std.mtof(76), Std.mtof(81), ] @=> float pitches[]; fun vec3 vecmul(vec3 a, vec3 b) { vec3 out; a.x * b.x => out.x; a.y * b.y => out.y; a.z * b.z => out.z; return out; } // setup the ChuGL scene GG.scene().enableFog(); GG.camera().posZ( 20 ); GG.scene().backgroundColor( @(0.2, 0.2, 0.2) ); // steup the ball GBall ball1, ball2; 0.5 => float radius; 4 => int numWalls; 0.2 => float wallThickness; 4 => float wallLength; wallLength / 2 => float wallOffset; wallThickness / 2 + radius / 2 => float collision_offset; 20::ms => dur updateInterval; 20.0 / 1000 => float dt; // setup sounds Beep beeps[numWalls]; Snare bops[numWalls]; Box box1 --> GG.scene(); box1.posX(-3); box1.setSound(beeps); box1.setBalls( @(0, 0, 0), @(-3, 0, 0) * 2, @(0, 0, 0), @(3, -1, 0) ); box1.updateBoxState(); Box box2 --> GG.scene(); box2.posX(3); box2.setSound(bops); box2.setBalls( @(0, 0, 0), @(-1, -2, 0) * 2, @(0, 0, 0), @(-2, -1, 0) ); box2.updateBoxState(); class GBall extends GSphere { vec3 p; vec3 v; vec3 wallLoc[]; vec3 wallHit[]; Instrument sounds[]; [0, 0, 0, 0] @=> int states[]; fun void udpateBallState(vec3 c, vec3 p0, vec3 v0, vec3 walloc[], vec3 walhit[]) { p0 => p; v0 => v; walloc @=> wallLoc; walhit @=> wallHit; this.mat().color(c); } fun void setSound(Instrument snds[]) { snds @=> sounds; } fun void setState(int sts[]) { sts @=> states; } 0 => int hit; fun void updatePhysicsBall(float dt) { // a * dt +=> v; // g * dt +=> v; v * dt +=> p; // collision check if(p.y < wallLoc[0].y + collision_offset) { vecmul(v, wallHit[0]) => v; 1 => hit; } else if(p.x > wallLoc[1].x - collision_offset) { vecmul(v, wallHit[1]) => v; 2 => hit; } else if(p.y > wallLoc[2].y - collision_offset) { vecmul(v, wallHit[2]) => v; 3 => hit; } else if(p.x < wallLoc[3].x + collision_offset) { vecmul(v, wallHit[3]) => v; 4 => hit; } else { 0 => hit; } this.pos(p); if( hit > 0 ){ // <<<"hit: ", hit>>>; hit - 1 => int idx; spork ~ sounds[idx].play( pitches[ states[idx] ] ); } } } class Box extends GGen { GCube walls[numWalls]; GBall ball1, ball2; vec3 position1, velocity1, position2, velocity2; Instrument sounds[]; [0, 0, 0, 0] @=> int states[]; fun void setBalls(vec3 p1, vec3 v1, vec3 p2, vec3 v2) { p1 => position1; v1 => velocity1; p2 => position2; v2 => velocity2; } fun void setSound(Instrument snds[]) { ball1.setSound(snds); ball2.setSound(snds); } // idx is wall index fun void changeState(int idx) { 1 +=> states[idx]; if( states[idx] > 7 ){ 0 => states[idx]; } setColor(); ball1.setState(states); ball2.setState(states); } fun void setColor() { for(int i; i < numWalls; i++) { walls[i].mat().color( palette[states[i]] / 255 ); } } setColor(); [ball1, ball2] @=> GBall balls[]; for (GBall b : balls) { b.sca( radius ); // b.mat(mat); b --> this; } // wall location vec3 wallLoc[numWalls]; @(0, -wallOffset, 0) => wallLoc[0]; @(wallOffset, 0, 0) => wallLoc[1]; @(0, wallOffset, 0) => wallLoc[2]; @(-wallOffset, 0, 0) => wallLoc[3]; // direction change vector for each wall when collision happens vec3 wallHit[numWalls]; @(1, -1, 0) => wallHit[0]; @(-1, 1, 0) => wallHit[1]; @(1, -1, 0) => wallHit[2]; @(-1, 1, 0) => wallHit[3]; fun void updateBoxState(){ ball1.udpateBallState( @(0, 0.5, 1), position1, velocity1, wallLoc, wallHit ); ball2.udpateBallState( @(1, 0.6, 0.1), position2, velocity2, wallLoc, wallHit ); } updateBoxState(); // ball1.init( @(0.5, 0, 0), @(0,0,0), @(0, -3, 0), wallLoc, wallHit ); // ball2.init( @(0, 0, 0.5), @(0, 1, 0), @(2, 3, 0), wallLoc, wallHit ); for(int i; i < numWalls; i++) { walls[i] --> this; walls[i].pos(wallLoc[i]); if( (i%2) == 0) { walls[i].sca(@(wallLength, wallThickness, 2)); } else { walls[i].sca(@(wallThickness, wallLength, 2)); } } fun void updatePhysicsBox(float dt) { while( true ) { for (GBall b : balls) { b.updatePhysicsBall(dt); } updateInterval => now; } } } // physics loop // fun void physicsUpdater() // { // box1.updatePhysicsBox(dt); // box2.updatePhysicsBox(dt); // } // spork ~ physicsUpdater(); spork ~ box1.updatePhysicsBox(dt); spork ~ box2.updatePhysicsBox(dt); ////// End of Sequencer code fun void KBListener() { while(true){ if( KB.isKeyDown(KB.KEY_1) ) { box1.changeState(0); } if( KB.isKeyDown(KB.KEY_2) ) { box1.changeState(1); } if( KB.isKeyDown(KB.KEY_3) ) { box1.changeState(2); } if( KB.isKeyDown(KB.KEY_4) ) { box1.changeState(3); } if( KB.isKeyDown(KB.KEY_5) ) { box2.changeState(0); } if( KB.isKeyDown(KB.KEY_6) ) { box2.changeState(1); } if( KB.isKeyDown(KB.KEY_7) ) { box2.changeState(2); } if( KB.isKeyDown(KB.KEY_8) ) { box2.changeState(3); } // keyboard even update interval 100::ms => now; } } spork ~ KBListener(); ////// GAME LOOP & Keyboard Monitoring while( true ) { // <<< "FPS: ", GG.fps() >>>; GG.nextFrame() => now; }