Created
December 10, 2013 02:15
-
-
Save MattRix/7884772 to your computer and use it in GitHub Desktop.
This was create for Keith Peters' now defunct 25lines AS3 competition. This entry fit within all the rules of the competition (ex no cheap chaining) To try it yourself, simply paste this code on the first frame of an empty FLA. To view it live, check it out here: http://struct.ca/share/rock
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [SWF (backgroundColor=0, width=800,height=600, frameRate=60)] | |
| ////////////////////////////////////////// | |
| //ROCK MUSIC featuring THE ROLLING STONE// | |
| ////////////////////////////////////////// | |
| //By Matt Rix // | |
| ////////////////////////////////////////// | |
| //Hold down your mouse on the left and // | |
| //right sides of the screen to move // | |
| //the stone. The closer you are to the // | |
| //edge of the screen, the more force // | |
| //you'll have. Let go of the mouse to // | |
| //let gravity take over. // | |
| // // | |
| //None of the variables are typed, so // | |
| //I ran into some huge speed problems // | |
| //when doing the audio stuff on // | |
| //thousands of samples. Because of that // | |
| //this toy requires a pretty fast // | |
| //computer to run smoothly. // | |
| ////////////////////////////////////////// | |
| //freebies | |
| stage.align = StageAlign.TOP_LEFT; | |
| stage.scaleMode = StageScaleMode.SHOW_ALL; | |
| //1: create a config object to store all of our config options as well as creating a handy "scope" reference. | |
| var scope:* = (this.config = {x:400, y:300, width:800, height:600, sceneWidth:10000, segmentWidth:8, terrainFlatness:0.01, musicTempo:150, musicKey:440, acceleration:.4, gravity:.4, maxSpeed:15, lastX:0, lastY:0, scope:this}).scope; | |
| //2: create the stone texture | |
| (scope.wheelBMD = new BitmapData(100,100,false,0)).perlinNoise(20,20,2,123,true,true,7,true); | |
| //3: create the wheel | |
| MovieClip(MovieClip(addChild(scope.level = new MovieClip())).addChild(scope.wheel = new MovieClip())).graphics.beginBitmapFill(scope.wheelBMD,null,true,false); | |
| //4: draw the wheel and init the terrain drawing vars | |
| scope.points = [new Point((scope.speedX = int(scope.wheel.graphics.drawCircle(int(scope.level.graphics.lineStyle(3,0x00FF00,1)),int(scope.level.graphics.moveTo(0,300)),30))), (scope.mountY = (scope.wheel.x = scope.config.height/2)+(scope.mountSpeed = (scope.lineIndex = 0))))]; | |
| //5: build an array of terrain points | |
| while((scope.points = scope.points.concat([new Point(scope.points.length*scope.config.segmentWidth, scope.mountY = (scope.config.height/2+(scope.mountY-scope.config.height/2)*.9) + (scope.mountSpeed = (1-scope.config.terrainFlatness)*(scope.mountSpeed + -3 + Math.random()*6)))])).length < Math.ceil(scope.config.sceneWidth/scope.config.segmentWidth)); | |
| //6: draw the terrain points | |
| while((scope.lineIndex = scope.lineIndex + 1 + int(scope.level.graphics.lineTo(scope.points[scope.lineIndex].x, scope.points[scope.lineIndex].y))) < scope.points.length); | |
| //7: listen for enter frames | |
| addEventListener(Event.ENTER_FRAME, function(e:Event):void | |
| { | |
| //8: move the wheel(the rotation value is the same as the x value) | |
| (scope.wheel.x = Math.max(0, Math.min(scope.config.sceneWidth-1, (scope.wheel.x + .5*((scope.speedX = Math.max(-scope.config.maxSpeed, Math.min(scope.config.maxSpeed, scope.speedX*.99 + int(scope.isMouseDown)*(scope.config.acceleration*2*(scope.mouseX-(scope.level.x + scope.wheel.x))/scope.config.width))))))))); | |
| //9: interpolate the terrain points to figure out what y-value the ground is at, and put the wheel on the ground | |
| scope.wheel.y = -30 + (scope.baseY = scope.points[Math.max(0, Math.min(Math.floor(scope.config.sceneWidth/scope.config.segmentWidth)-1, Math.floor(scope.wheel.x/scope.config.segmentWidth)))].y)+ (scope.deltaY = (scope.points[Math.max(0, Math.min(Math.floor(scope.config.sceneWidth/scope.config.segmentWidth)-1, Math.floor(scope.wheel.x/scope.config.segmentWidth)+1))].y - scope.points[Math.max(0, Math.min(200000, Math.floor(scope.wheel.x/scope.config.segmentWidth)))].y)) * ((scope.wheel.x%scope.config.segmentWidth)/scope.config.segmentWidth); | |
| //10: figure out the ground's angle and apply gravity | |
| scope.speedX += Math.sin(Math.atan2(scope.deltaY,scope.config.segmentWidth))*scope.config.gravity; | |
| //11: rotate the wheel based on our direction and speed | |
| scope.wheel.rotation += scope.speedX/Math.abs(scope.speedX) * Math.sqrt((scope.config.lastX - scope.wheel.x) * (scope.config.lastX - scope.wheel.x) + (scope.config.lastY - scope.wheel.y) * (scope.config.lastY - scope.wheel.y)) + (scope.config.lastX = scope.wheel.x)*0 + (scope.config.lastY = scope.wheel.y)*0; | |
| //12: move and ease the "camera" x | |
| scope.level.x += ((scope.config.width/2 - scope.wheel.x) - scope.level.x)/30; | |
| //13: move and ease the "camera" y | |
| scope.level.y += ((scope.config.height/2 - scope.wheel.y) - scope.level.y)/30; | |
| }); | |
| //14: listen for mouse downs | |
| stage.addEventListener(MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void { scope.isMouseDown = true}); | |
| //15: listen for mouse ups | |
| stage.addEventListener(MouseEvent.MOUSE_UP, function(e:MouseEvent):void { scope.isMouseDown = false}); | |
| //16: create the music config. This only counts as a single line, because if I took out the line breaks, it would be :) | |
| //it creates a bunch of "instrument" settings that define the 3 instruments that play. Fiddling around with these variables leads to tons of fun stuff | |
| scope.music = | |
| { | |
| numSamples:2048, | |
| scale:[2,2,3,2,3], //major pentatonic scale | |
| sample:0, | |
| instrument: | |
| [ | |
| /*MELODY*/{freq:0, position:0, noteLength:Math.round(1/4 *44100*60/scope.config.musicTempo*4), beatOffset:0, phase:0, volume:.4, envVolume:0, percent:0, sample:0, currentNote:0, mouseRange:30, numNotes: 21, startNote:20, baseFreq:scope.config.musicKey/4, freqs:[]}, | |
| /*BASS*/{freq:0, position:0, noteLength:Math.round(8/4 *44100*60/scope.config.musicTempo*4), beatOffset:0, phase:0, volume:.3, envVolume:0, percent:0, sample:0, currentNote:0, mouseRange:100, numNotes: 12, startNote:7, baseFreq:scope.config.musicKey/8, freqs:[]}, | |
| /*TINKLES*/{freq:0, position:0, noteLength:Math.round(3/8 *44100*60/scope.config.musicTempo*4), beatOffset:0, phase:0, volume:.2, envVolume:0, percent:0, sample:0, currentNote:0, mouseRange:1, numNotes: 6, startNote:5, baseFreq:scope.config.musicKey/1, freqs:[]}, | |
| ] | |
| }; | |
| //17: generate an array of all the needed frequencies in a scale | |
| for each(var instrument:Object in scope.music.instrument) while((instrument.freqs = instrument.freqs.concat([(instrument.baseFreq = instrument.baseFreq * Math.pow(1.05946309435929, scope.music.scale[instrument.freqs.length%scope.music.scale.length]))])).length < instrument.numNotes); | |
| //18: create a nwe dynamic sound and start listening to it | |
| (scope.dynamicSound = new Sound()).addEventListener(SampleDataEvent.SAMPLE_DATA, function (e:SampleDataEvent):void | |
| { | |
| //19: loop through the samples | |
| for(var i:int = 0; i<scope.music.numSamples; i++) | |
| { | |
| //20: loop through the instrument information | |
| for each(var instrument:Object in scope.music.instrument) | |
| { | |
| //21: if we're on a beat, get a new frequency for the note based on the ball's y position | |
| if(((instrument.position = instrument.position+1) + instrument.beatOffset)%instrument.noteLength == 0) instrument.freq = Math.min(scope.config.musicKey*4, instrument.freqs[((instrument.currentNote = instrument.startNote-Math.round(scope.wheel.y/instrument.mouseRange))+instrument.freqs.length*100000)%instrument.freqs.length]); | |
| //I used to have a ton of sweet volume envelope stuff in here(attack, hold, release), but the untyped references made everything run so slow that I couldn't get more than one instrument at a time without it conking out | |
| //22: add this instruments's samples to the full sample | |
| scope.music.sample += ((Math.sin((instrument.phase = instrument.phase + instrument.freq/44100)*Math.PI*2)) * instrument.volume * 0.2 * (0.7+0.3*Math.abs(scope.speedX)/15)); | |
| } | |
| //23: write the samples to the audio buffer. I was trying to come up with a way to combine the bytes of these two floats and write them as a double, but it's tricky, and this is much simpler | |
| e.data.writeFloat(scope.music.sample + (scope.music.sample = int(e.data.writeFloat(scope.music.sample)))); | |
| } | |
| }); | |
| //24: add this instruments's samples to the full sample | |
| (scope.dynamicSoundChannel = scope.dynamicSound.play()); | |
| //25: I left the 25th line blank in case I accidentally broke a rule somewhere. I know I got creative with chaining, but I made sure to never do any cheap recursive/infinite stuff. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment