/* //<>// Interactive Toy Assignment Orbit Nathan Powless-Lynes@2015.10.7 A series of planet-like objects interact with each other due to gravity. Gravity works just like it does in reality, using the same formula from physics, except the gravitational constant is different. The player is also able to contribute to the largest planet's movement using WASD, making for limetless opportunities to play with the orbits of the moons. The player also has the ability to reverse their own planet's gravity using space, allowing them to push away the moons if they get too close. This is a toy, so there are no definite goals. If you like, you can set them for yourself. For example: - Can I get the moons to resume orbit around me after they have stopped? - Can I get the green moon to orbit the red moon without my planet interfering? - Can I get the green moon to orbit me really closely, and then launch it at the red moon by reversing gravity? How you choose to play Orbit is up to you! Have fun! Version History: Version 1.0: the player's planet and a moon to orbit it. The player's planet is slightly affected greenPositionY gravity, but is mostly controlled greenPositionY the player. Version 1.1: adds a second moon, made eveDistanceY object attracted to each other.playerPositionX Version 1.2: made the player unattracted to the moons. Version 1.3: placed the moons perfectly so moon b orbits moon a and moon a orbits the player. Also added puffs of smoke that come out of the player when accelerating Version 1.4: changed the colour of moon a to red. Moons are now referred to as Red and Green. Also added the ability to reverse the force of gravity coming from the player Version 1.5: added a starry background. Made pulses come out of the player, illustrating the pull or push of gravity Version 1.6 (current): fixed issue that made it difficult to maintain orbit. Added separate function for determining distance. Also added instructional text that fades in, but dissappears when the player proves they know how the controls work by moving and reversing gravity with WASD and space */ //declaring global variables /* player variables These are variables that determine how big and massive the player will be. The player's mass will affect how much the other planets are attracted to thie player. Other variables indicate the player's position and speed in both directions. */ float playerPositionX;//x-coordinate float playerPositionY;//y-coordinate float playerSpeedX;//speed in the x-direction float playerSpeedY;//speed in the y-direction float playerMass;//mass (affects attraction) float playerSize;//width and height /* moon variables These are the variables for the red and green moons. They show how big and massive they are. The red moon has a greater mass, so it is more attracted to the player than the green moon is. Their speed and position for both directions are included as well. */ //red variables float redPositionX;//x-coordinate float redPositionY;//y-coordinate float redSpeedX;//speed in the x-direction float redSpeedY;//speed in the y-direction float redMass;//mass (affects attraction) float redSize;//width and height //green variables float greenPositionX;//x-coordinate float greenPositionY;//y-coordinate float greenSpeedX;//speed in the x-direction float greenSpeedY;//speed in the y-direction float greenMass;//mass (affects attraction) float greenSize;//width and height /* directional controls variables These variables are used to show which direction(s) the player is accelerating in. They are activated by WASD, allowing the player to take control of their planet. */ boolean accelerateRight = false;//true when accelerating right boolean accelerateLeft = false;//true when accelerating left boolean accelerateUp = false;//true when accelerating up boolean accelerateDown = false;//true when accelerating down /* distance variables These variables are used when measuring the distance between the planets. The distance is used for the formulas for attraction, furthermore deciding whether or not attraction occurs at all. */ //from the player to the red moon float redPlayerDistance;//magnitude float redPlayerDistanceX;//in x-direction float redPlayerDistanceY;//in y-direction //from the player to the green moon float greenPlayerDistance;//magnitude float greenPlayerDistanceX;//in x-direction float greenPlayerDistanceY;//in y-direction //from the red moon to the green moon float redGreenDistance;//magnitude float redGreenDistanceX;//in x-direction float redGreenDistanceY;//in y-direction /* reversal of gravity This variable is used to test whether gravity is reversed or not. Reverse gravity is activated by pressing the space bar. When gravity is reversed, rather than attracting the moons, the player's planet repels them. */ boolean reverseGravity;//true when player repels the moons /* gravitational constant This variable is used in the gravity formulas. Although technically might not be necessary, to make the gravity more like reality, a constant is needed. This formula is taken straight from grade 12 physics, so it is a legitimate gravity formula. The constant is changed from reality to make it easier to achieve orbit. */ float G; /* pulse variables The player's planet emmits pulses every so often, and a variable is used to measure the time since the last pulse. There is another variable that represents the duration of each pulse. Both variables are used in determining the intensity of the colour of the pulse. */ int pulseTime; int pulseDuration; /* smoke variables It may seem like there are a lot of variables for the smoke, but it's really just the same 3 variables repeated once for each puff of smoke. The first two determine the x and y position of the puff, and the third modifies the translucency, width and height of the puff. There are also variables that are nonspecific to each puff. One acts as a timer, showing when the last puff was made. Another keeps track of which puff is to be drawn next in the sequence. The last variable merely shows when the "engine" is on, or when the player is accelerating. */ int lastPuff = 0;//lastPuff since last puff //three coordinates for puffs so we can have three on the screen at a time float puff1PositionX;//x-coordinate float puff1PositionY;//y-coordinate float puff2PositionX;//x-coordinate float puff2PositionY;//y-coordinate float puff3PositionX;//x-coordinate float puff3PositionY;//y-coordinate float puff1Mod;//modifies alpha and width float puff2Mod;//modifies alpha and width float puff3Mod;//modifies alpha and width byte whichPuff;//shows which puff in the sequence must be drawn next boolean engineOn;//whether or not the player is accelerating /* text variable Variables that say whether the player has moved or reversed gravity yet. These are used to determine whether or not the instructional text should be displayed. */ boolean moved; boolean reversedOnce; //setting up the canvas and initializing variables void setup() { /* set up Declare the canvas size and get rid of the stroke. Also draw a background. */ size(400, 400);//canvas size noStroke(); background(0); /* initializing variables Just has all the variables initialized at the same place. The functions of the variables are all specified above. I could have initialized them up there, but this hopefully makes the program more readable. */ playerSpeedX = 0; playerSpeedY = 0; playerMass = 20; playerSize = 30; playerPositionX = width/2; playerPositionY = height/2; redSpeedX = -1.95; redSpeedY = 0; redMass = 10; redSize = 20; redPositionX = width/2; redPositionY = height/2 - 100; greenSpeedX = -4; greenSpeedY = 0; greenMass = 5; greenSize = 10; greenPositionX = width/2; greenPositionY = height/2 - 120; reverseGravity = false; G = 2; pulseTime = 0; pulseDuration = 1200; whichPuff = 1; engineOn = false; moved = false; reversedOnce = false; } /* draw Updates positions, speeds and acceleration of the planets and draws everything in the game. Also draws text if the player has not used the controls yet. */ void draw() { //UPDATE //check how far the planets are from each other updateDistance(); //check acceleration and adjust speed redToPlayer(); redToGreen(); greenToPlayer(); playerAcceleration(); //update the planetoids' current position updatePlayerPosition(); updateRedPosition(); updateGreenPosition(); //update the puffs of smoke updateSmoke(); //DRAW //a translucent background, resulting in trails as the planets move fill(0, 0, 0, 60); rect(0, 0, width, height); //check if the player has figured out the controls yet. If not, //tell them the controls if(!moved) { fill(constrain(0.05*millis(), 0, 255)); text("Use WASD to move", 10, 40); } if(!reversedOnce) { fill(constrain(0.05*millis(), 0, 255)); text("Hold space to reverse your gravity", width - 190, 40); } //draw the stars drawStars(); //draw the moons drawGreen(); drawRed(); //draw the player drawPulse(); drawSmoke(); drawPlayer(); } //measure the distance between each planet void updateDistance() { //distance is the difference in their coordinates redPlayerDistanceX = playerPositionX - redPositionX; redPlayerDistanceY = playerPositionY - redPositionY; greenPlayerDistanceX = playerPositionX - greenPositionX; greenPlayerDistanceY = playerPositionY - greenPositionY; redGreenDistanceX = redPositionX - greenPositionX; redGreenDistanceY = redPositionY - greenPositionY; //magnitude of distance (pythagorean theorem) redPlayerDistance = sqrt(redPlayerDistanceX*redPlayerDistanceX + redPlayerDistanceY*redPlayerDistanceY); greenPlayerDistance = sqrt(greenPlayerDistanceX*greenPlayerDistanceX + greenPlayerDistanceY*greenPlayerDistanceY); redGreenDistance = sqrt(redGreenDistanceX*redGreenDistanceX + redGreenDistanceY*redGreenDistanceY); } //checks attraction between the red moon and the player void redToPlayer() { //variables //local gravity float g;//magnitude float gx;//in x-direction float gy;//in y-direction //make sure they are close enough together to issue attraction if (redPlayerDistance < 200) { //universal formula for gravity g = (G*playerMass*redMass)/(redPlayerDistance*redPlayerDistance); //if gravity is reversed, repel instead of attracting if (reverseGravity) { gx = -abs(g*(redPlayerDistanceX/redPlayerDistance)); gy = -abs(g*(redPlayerDistanceY/redPlayerDistance)); } else { gx = abs(g*(redPlayerDistanceX/redPlayerDistance)); gy = abs(g*(redPlayerDistanceY/redPlayerDistance)); } //accelerating in x-direction if (playerPositionX > redPositionX) { //playerSpeedX = constrain(playerSpeedX - 0.1*gx, -8, 8); redSpeedX = constrain(redSpeedX + gx, -8, 8); } else { //playerSpeedX = constrain(playerSpeedX + 0.1*gx, -8, 8); redSpeedX = constrain(redSpeedX - gx, -8, 8); } //accelerating in y-direction if (playerPositionY > redPositionY) { //playerSpeedY = constrain(playerSpeedY - 0.1*gy, -8, 8); redSpeedY = constrain(redSpeedY + gy, -8, 8); } else { //playerSpeedY = constrain(playerSpeedY + 0.1*gy, -8, 8); redSpeedY = constrain(redSpeedY - gy, -8, 8); } }//if too far from both other planets, slow it down else if (redGreenDistance > 200) { redSpeedX = 0.99*redSpeedX; redSpeedY = 0.99*redSpeedY; } } //checks attraction between player and the green moon void greenToPlayer() { //variables //local gravity float g;//magnitude float gx;//in x-direction float gy;//in y-direction //make sure they are close enough together to issue attraction if (greenPlayerDistance < 200) { //universal formula for gravity g = (G*playerMass*greenMass)/(greenPlayerDistance*greenPlayerDistance); //if gravity is reversed, repel instead of attracting if (reverseGravity) { gx = -abs(g*(greenPlayerDistanceX/greenPlayerDistance)); gy = -abs(g*(greenPlayerDistanceY/greenPlayerDistance)); } else { gx = abs(g*(greenPlayerDistanceX/greenPlayerDistance)); gy = abs(g*(greenPlayerDistanceY/greenPlayerDistance)); } //accelerating in x-direction if (playerPositionX > greenPositionX) { //playerSpeedX = constrain(playerSpeedX - 0.1*gx, -8, 8); greenSpeedX = constrain(greenSpeedX + gx, -8, 8); } else { //playerSpeedX = constrain(playerSpeedX + 0.1*gx, -8, 8); greenSpeedX = constrain(greenSpeedX - gx, -8, 8); } //accelerating in y-direction if (playerPositionY > greenPositionY) { //playerSpeedY = constrain(playerSpeedY - 0.1*gy, -8, 8); greenSpeedY = constrain(greenSpeedY + gy, -8, 8); } else { //playerSpeedY = constrain(playerSpeedY + 0.1*gy, -8, 8); greenSpeedY = constrain(greenSpeedY - gy, -8, 8); } }//if too far from both other planets, slow it down else if (redGreenDistance > 200) { greenSpeedX = 0.99*greenSpeedX; greenSpeedY = 0.99*greenSpeedY; } } //checks attraction between the moons void redToGreen() { //variables //local gravity float g;//magnitude float gx;//in x-direction float gy;//in y-direction //make sure they are close enough together to issue attraction if (redGreenDistance < 200) { //universal formula for gravity g = (G*redMass*greenMass)/(redGreenDistance*redGreenDistance); gx = abs(g*(redGreenDistanceX/redGreenDistance)); gy = abs(g*(redGreenDistanceY/redGreenDistance)); //accelerating in x-direction if (redPositionX > greenPositionX) { redSpeedX = constrain(redSpeedX - 0.1*gx, -8, 8); greenSpeedX = constrain(greenSpeedX + gx, -8, 8); } else { redSpeedX = constrain(redSpeedX + 0.1*gx, -8, 8); greenSpeedX = constrain(greenSpeedX - gx, -8, 8); } //accelerating in y-direction if (redPositionY > greenPositionY) { redSpeedY = constrain(redSpeedY - 0.1*gy, -8, 8); greenSpeedY = constrain(greenSpeedY + gy, -8, 8); } else { redSpeedY = constrain(redSpeedY + 0.1*gy, -8, 8); greenSpeedY = constrain(greenSpeedY - gy, -8, 8); } } else { //if red is too far from both other planets, slow it down if(redPlayerDistance > 200) { redSpeedX = 0.99*redSpeedX; redSpeedY = 0.99*redSpeedY; } //if green is too far from both other planets, slow it down if(greenPlayerDistance > 200) { greenSpeedX = 0.99*greenSpeedX; greenSpeedY = 0.99*greenSpeedY; } } } /* updateSmoke Uses randomness to assign a place for each of the three puffs of smoke. Uses a variable to keep track of the puff that it is referring to. This makes it so three puffs can appear at once. */ void updateSmoke() { //setting up smoke from the player's "engine" if (engineOn) { //for the puffs of smoke if (lastPuff == 0) { lastPuff = millis(); } if (lastPuff != 0 && (millis() - lastPuff) >= 100 - 2*sqrt(playerSpeedX*playerSpeedX + playerSpeedY*playerSpeedY)) { if (whichPuff == 1) { puff1PositionX = playerPositionX + random(-0.3*playerSize, 0.3*playerSize); puff1PositionY = playerPositionY + random(-0.3*playerSize, 0.3*playerSize); puff1Mod = 200; whichPuff = 2; } else if (whichPuff == 2) { puff2PositionX = playerPositionX + random(-0.3*playerSize, 0.3*playerSize); puff2PositionY = playerPositionY + random(-0.3*playerSize, 0.3*playerSize); puff2Mod = 200; whichPuff = 3; } else { puff3PositionX = playerPositionX + random(-0.3*playerSize, 0.3*playerSize); puff3PositionY = playerPositionY + random(-0.3*playerSize, 0.3*playerSize); puff3Mod = 200; whichPuff = 1; } lastPuff = millis(); } } else { lastPuff = 0; } } //draw the player void drawPlayer() { //the glow fill(0, 100, 255); ellipse(playerPositionX, playerPositionY, playerSize + 1.5, playerSize + 1.5); //body fill(0, 50, 255); ellipse(playerPositionX, playerPositionY, playerSize, playerSize); //the eye fill(0, 100, 255); ellipse(playerPositionX, playerPositionY, 3*playerSize/5, 3*playerSize/5); } //draws the smoke that comes out of the player void drawSmoke() { //drawing the smoke //puff1 fill(130, 150, 255, puff1Mod); ellipse(puff1PositionX, puff1PositionY, 0.2*(255 - puff1Mod), 0.2*(255 - puff1Mod)); puff1Mod -= 5; //puff2 fill(150, 130, 255, puff2Mod); ellipse(puff2PositionX, puff2PositionY, 0.2*(255 - puff2Mod), 0.2*(255 - puff2Mod)); puff2Mod -= 5; //puff3 fill(150, 150, 235, puff3Mod); ellipse(puff3PositionX, puff3PositionY, 0.2*(255 - puff3Mod), 0.2*(255 - puff3Mod)); puff3Mod -= 5; } //draws the "gravity pulses" coming out of the player void drawPulse() { //check time since last pulse if (millis() - pulseTime >= pulseDuration || pulseTime == 0) { //reset timer pulseTime = millis(); } //colour of pulses depends on distance noFill(); strokeWeight(20); //check if the player's gravity is reversed if (reverseGravity) { stroke(255 - redPlayerDistance, 255 - greenPlayerDistance, 255, 100 + 0.1*(pulseTime - millis())); ellipse(playerPositionX, playerPositionY, 400*(millis() - pulseTime)/pulseDuration, 400*(millis() - pulseTime)/pulseDuration); } else { stroke(255 - redPlayerDistance, 255 - greenPlayerDistance, 255, -20 - 0.1*(pulseTime - millis())); ellipse(playerPositionX, playerPositionY, 400 - 400*(millis() - pulseTime)/pulseDuration, 400 - 400*(millis() - pulseTime)/pulseDuration); } noStroke(); } //locate and draw the red moon void drawRed() { //the glow fill(255, 60, 0); ellipse(redPositionX, redPositionY, redSize+1.5, redSize+1.5); //the body fill(160, 30, 0); ellipse(redPositionX, redPositionY, redSize, redSize); //the eye fill(200, 30, 0); ellipse(redPositionX, redPositionY, 3*redSize/5, 3*redSize/5); } //locate and draw the green moon void drawGreen() { //the glow fill(122, 255, 0); ellipse(greenPositionX, greenPositionY, greenSize+1.5, greenSize+1.5); //the body fill(60, 120, 0); ellipse(greenPositionX, greenPositionY, greenSize, greenSize); //the eye fill(75, 150, 0); ellipse(greenPositionX, greenPositionY, 3*greenSize/5, 3*greenSize/5); } //checks for manual acceleration of the player void playerAcceleration() { //vertical if (accelerateUp) { playerSpeedY -= 0.1; } if (accelerateDown) { playerSpeedY += 0.1; } //horizontal if (accelerateRight) { playerSpeedX += 0.1; } if (accelerateLeft) { playerSpeedX -= 0.1; } playerSpeedX = 0.99 * playerSpeedX; playerSpeedY = 0.99 * playerSpeedY; } /* updatePlayerPosition Set the player's position by adding their speed to their previous position. Also constrains the value so they won't go off-screen. */ void updatePlayerPosition() { //adjust position based on speed playerPositionY = constrain(playerPositionY + playerSpeedY, 0 + playerSize/2, height - playerSize/2); playerPositionX = constrain(playerPositionX + playerSpeedX, 0 + playerSize/2, width - playerSize/2); if (playerPositionX >= width - playerSize/2 || playerPositionX <= 0 + playerSize/2) { playerSpeedX = -playerSpeedX; } if (playerPositionY >= height - playerSize/2 || playerPositionY <= 0 + playerSize/2) { playerSpeedY = -playerSpeedY; } } /* update RedPosition Set the red moon's position by adding its speed to its previous position. Also constrains the value so it won't go off-screen. */ void updateRedPosition() { redPositionX = constrain(redPositionX + redSpeedX, 0 + redSize/2, width - redSize/2); redPositionY = constrain(redPositionY + redSpeedY, 0 + redSize/2, height - redSize/2); if (redPositionX >= width - redSize/2 || redPositionX <= 0 + redSize/2) { redSpeedX = -redSpeedX; } if (redPositionY >= height - redSize/2 || redPositionY <= 0 + redSize/2) { redSpeedY = -redSpeedY; } } /* updateGreenPosition Set the green moon's position by adding its speed to its previous position. Also constrains the value so it won't go off-screen. */ void updateGreenPosition() { greenPositionX = constrain(greenPositionX + greenSpeedX, 0 + greenSize/2, width - greenSize/2); greenPositionY = constrain(greenPositionY + greenSpeedY, 0 + greenSize/2, height - greenSize/2); if (greenPositionX >= width - greenSize/2 || greenPositionX <= 0 + greenSize/2) { greenSpeedX = -greenSpeedX; } if (greenPositionY >= height - greenSize/2 || greenPositionY <= 0 + greenSize/2) { greenSpeedY = -greenSpeedY; } } /* drawStars This draws the stars using a loop. Normally, this would result in a grid-like pattern, a variable is used to offset the stars every so often, resulting in a Milky Way style pattern */ void drawStars() { //a variable that counts the number of stars placed, some being offset byte starCount = 0; //draw stars for (int x = 0; x < (width/40 + 1); ++x) { for (int y = 0; y < (height/40 + 2); ++y) { fill(random(100, 170)); //check offset if (starCount == 1) { ellipse(48*x - 10*y, 42*y + 3*x, 3, 3); ++starCount; } else if (starCount == 4) { ellipse(32*x - 10*y, 39*y + 3*x, 3, 3); starCount = 0; } else { ellipse(45*x - 10*y, 40*y + 3*x, 3, 3); ++starCount; } } } } //checks for a key press void keyPressed() { if (key == 'w' || key == 'W') { moved = true; accelerateUp = true; engineOn = true; } if (key == 'a' || key == 'A') { moved = true; accelerateLeft = true; engineOn = true; } if (key == 's' || key == 'S') { moved = true; accelerateDown = true; engineOn = true; } if (key == 'd' || key == 'D') { moved = true; accelerateRight = true; engineOn = true; } if (key == ' ') { reversedOnce = true; if (!reverseGravity) { pulseTime = millis() - pulseTime; reverseGravity = true; } } } //checks if a key has been released void keyReleased() { if (key == 'w' || key == 'W') { accelerateUp = false; } if (key == 'a' || key == 'A') { accelerateLeft = false; } if (key == 's' || key == 'S') { accelerateDown = false; } if (key == 'd' || key == 'D') { accelerateRight = false; } if (key == ' ') { if (reverseGravity) { pulseTime = millis() - pulseTime; reverseGravity = false; } } //checks if the "engine" is still on if (!accelerateUp && !accelerateDown && !accelerateLeft && !accelerateRight) { //resets the "puff machine" eveDistanceY lastPuff the "engine" stops lastPuff = 0; engineOn = false; } }