diff --git a/Project/Game/Game.pde b/Project/Game/Game.pde index ef1af10..7b053a1 100644 --- a/Project/Game/Game.pde +++ b/Project/Game/Game.pde @@ -1,222 +1,302 @@ import java.text.DecimalFormat; Mover mover; //The ball Cylinder cyl; //The basis of all cylinders +HScrollbar hs; PImage img; PShape globe; PShape robot; //The vilain -float depth = 2000; //the depth from the box +float depth = 2000; //the depth from the box + +//Score; +ArrayList scores; +float score; +float scoreMalus = -1; +int scoreTimer = 0; +int prVeloFreq = 20; //The frequency at which the velocity is printed +float lastScore; +float veloMag; +float scoreFactor = 0.5; //lastScore = scoreFactor * lastVelocity; +boolean win = false; +int minHistowidth = 1,maxHistowidth = 50, histowidth; +float maxScore = 10, minScore=-10; +float drawingFactor; // = histoHeight /(maxScore -minScore); +int barChartWidth, barChartHeight; +boolean mousePermittedToMove=true; //Plate size float plate_w=1000; float plate_t=plate_w/20; float x_coord, y_coord; float mX, mY; //mouse X position float xOnPlane, yOnPlane; //coordinate on 2D on the box float rz, rx; //rotate x and rotate z float cameraHeight; float speed=10; float radius=plate_t*4/5; -DecimalFormat f = new DecimalFormat("#0.0"); +DecimalFormat f = new DecimalFormat("#0.00"); DecimalFormat f2 = new DecimalFormat("#0"); boolean shiftMod=false; ParticleSystem partSys=null; long previousFrame=0; float timeIntervalParticle=0.5;//seconds +int margin = 15; //marge between surface +int botPartHeight = 300; +int topViewWidth = botPartHeight-0; PGraphics gameSurface; PGraphics statSurface; PGraphics topView; +PGraphics scoreboard; +PGraphics barChart; void settings() { - size(displayWidth, displayHeight, P3D); + size(displayWidth, displayHeight-60, P3D); x_coord=width/2; y_coord=height/2; } void setup() { mover = new Mover(); cyl=new Cylinder(); + scores = new ArrayList(); + hs = new HScrollbar(750, height -50, 500, 20); img = loadImage("earth.jpg"); globe = createShape(SPHERE, radius); globe.setStroke(false); globe.setTexture(img); img = loadImage("robotnik.png"); robot = loadShape("robotnik.obj"); robot.setTexture(img); - gameSurface=createGraphics(width, height-300, P3D); - topView=createGraphics(width, 300, P2D); - statSurface=createGraphics(width, 100, P2D); + gameSurface=createGraphics(width, height-botPartHeight, P3D); + topView=createGraphics(topViewWidth, topViewWidth, P2D); + statSurface=createGraphics(3*botPartHeight, botPartHeight/3, P2D); + scoreboard = createGraphics(botPartHeight-2*margin,botPartHeight-2*margin, P2D); + + barChartWidth = width - (botPartHeight+margin) *2; + barChartHeight = botPartHeight-2*margin-50; + barChart = createGraphics(barChartWidth, barChartHeight, P2D); } void draw() { - background(200); - + background(181,65,2); + drawGame(); image(gameSurface, 0, 0); - + drawTopView(); - image(topView, 0, height-300); + image(topView, margin , height-botPartHeight); drawStat(); image(statSurface, 0, 0); + + drawScoreboard(); + image(scoreboard, botPartHeight+2*margin, height-botPartHeight+margin); + + drawBarChart(); + image(barChart, 2*botPartHeight+margin, height-botPartHeight+margin); + + hs.update(); + hs.display(); +} + +void drawBarChart(){ + barChart.beginDraw(); + barChart.background(242,86,2); + barChart.fill(40, 10, 200); + histowidth = (int)(hs.getPos()*(maxHistowidth-minHistowidth)+minHistowidth); + int maxRect = barChartWidth / histowidth; + int start = (scores.size() < maxRect)? 0 : scores.size()-maxRect; + for(int i = start ; i < scores.size(); ++i){ + float zeroInHisto = drawingFactor * (maxScore); + float posInHisto = drawingFactor * (-scores.get(i)+maxScore) ; + float top = (zeroInHistoposInHisto)?zeroInHisto:posInHisto)-top; + barChart.rect((i-start)*histowidth, top, histowidth, bot); + //println("i : "+i+" score : "+scores.get(i) +" top : "+top+" bot : "+bot); + } + barChart.endDraw(); +} +void updateScore(float points){ + score += points; + scores.add(score); + minScore = (scoremaxScore)? score : maxScore; + drawingFactor = barChartHeight/(maxScore-minScore+2); } +void drawScoreboard(){ + scoreboard.beginDraw(); + scoreboard.background(242,86,2); + scoreTimer = (scoreTimer%prVeloFreq == prVeloFreq-1) ? 0 : ++scoreTimer; + veloMag = (scoreTimer == 0)? mover.velocity.mag():veloMag; + scoreboard.fill(0); + scoreboard.textSize(25); + scoreboard.text("Total Score : \n "+f.format(score), 15,40); + scoreboard.text("Velocity : \n "+f.format(veloMag),15,130); + scoreboard.text("Last score : \n "+f.format(lastScore),15,220); + + scoreboard.endDraw(); +} void drawTopView() { topView.beginDraw(); - topView.background(200); + topView.background(181,65,2); topView.fill(0, 150, 150); - topView.rect(10, 0, 300, 300); + topView.rect(0, 0, topViewWidth, topViewWidth); topView.fill(255, 255, 255); - float topViewCylRad=map(cyl.cylinderBaseSize, 0, plate_w/2, 0, 300); + float topViewCylRad=map(cyl.cylinderBaseSize, 0, plate_w/2, 0, topViewWidth); if (partSys!=null) { for (PVector pv : partSys.cylinders) { - float topViewCylX=map(pv.x, -plate_w/2, plate_w/2, 10, 310); - float topViewCylY=map(pv.y, -plate_w/2, plate_w/2, 10, 310); + float topViewCylX=map(pv.x, -plate_w/2, plate_w/2, 0, topViewWidth); + float topViewCylY=map(pv.y, -plate_w/2, plate_w/2, 0, topViewWidth); topView.ellipse(topViewCylX, topViewCylY, topViewCylRad, topViewCylRad); } if (partSys.cylinders.contains(partSys.mainCyl)) { topView.fill(255, 0, 0); - float topViewCylX=map(partSys.mainCyl.x, -plate_w/2, plate_w/2, 10, 310); - float topViewCylY=map(partSys.mainCyl.y, -plate_w/2, plate_w/2, 10, 310); + float topViewCylX=map(partSys.mainCyl.x, -plate_w/2, plate_w/2, 0, topViewWidth); + float topViewCylY=map(partSys.mainCyl.y, -plate_w/2, plate_w/2, 0, topViewWidth); topView.ellipse(topViewCylX, topViewCylY, topViewCylRad, topViewCylRad); } } topView.fill(0,0,255); - float topViewBallRad=map(radius, 0, plate_w/2, 0, 300); - float topViewBallX=map(mover.location.x, -plate_w/2, plate_w/2, 10, 310); - float topViewBallY=map(mover.location.y, -plate_w/2, plate_w/2, 10, 310); + float topViewBallRad=map(radius, 0, plate_w/2, 0, topViewWidth); + float topViewBallX=map(mover.location.x, -plate_w/2, plate_w/2, 0, topViewWidth); + float topViewBallY=map(mover.location.y, -plate_w/2, plate_w/2, 0, topViewWidth); topView.ellipse(topViewBallX, topViewBallY, topViewBallRad, topViewBallRad); topView.endDraw(); } void drawStat() { statSurface.beginDraw(); statSurface.background(0, 0, 0, 0); statSurface.fill(0); statSurface.textSize(25); statSurface.text("Rotation X : "+f.format(rx/Math.PI*180)+"° Rotation Z : "+f.format(rz/Math.PI*180)+"° speed: "+f.format(speed/10), 5, 25); statSurface.text("Ball location : ("+f2.format(mover.location.x)+","+f2.format(mover.location.y)+")", 5, 50); statSurface.text("Ball velocity : ("+f2.format(mover.velocity.x)+","+f2.format(mover.velocity.y)+")", 5, 75); statSurface.endDraw(); } void drawGame() { gameSurface.beginDraw(); gameSurface.background(100, 0, 0); gameSurface.directionalLight(50, 100, 125, 0, 1, 0); gameSurface.ambientLight(102, 102, 102); gameSurface.pointLight(150, 150, 150, 0, -200, 0); if (!shiftMod) { mover.update(); mover.checkEdges(); if (partSys!=null) { mover.checkCylinderCollision(partSys.cylinders, radius); } rz = map(x_coord, 0, width, -PI/3, PI/3); rx = map(y_coord, 0, height, -PI/3, PI/3); cameraHeight=400; } else { rz=0; rx=-PI/2; cameraHeight=0; } gameSurface.pushMatrix(); gameSurface.camera(0, -cameraHeight, depth, 0, 0, 0, 0, 1, 0); gameSurface.rotateZ(rz); gameSurface.rotateX(rx); gameSurface.shininess(100); - gameSurface.box(plate_w, plate_t, plate_w); //particles if (partSys!=null) { if (frameCount-previousFrame>=timeIntervalParticle*frameRate && !shiftMod) { timeIntervalParticle=random(0.2, 1.2); previousFrame=frameCount; partSys.addParticle(); } partSys.run(-(plate_t/2), PI/2); } if (shiftMod) { - float wMappedX=2*plate_w; - float wMappedY=1.1*plate_w; - xOnPlane=map(mouseX, 0, width, -wMappedX, wMappedX); - yOnPlane=map(mouseY, 0, height, -wMappedY, wMappedY); + xOnPlane=map(mouseX,gameSurface.screenX(-plate_w/2,0,0),gameSurface.screenX(plate_w/2,0,0),-plate_w/2,plate_w/2); + yOnPlane=map(mouseY,gameSurface.screenY(0,0,-plate_w/2),gameSurface.screenY(0,0,plate_w/2),-plate_w/2,plate_w/2); cyl.draw(xOnPlane, yOnPlane, -(plate_t/2), PI/2); } gameSurface.translate(mover.getX(), -(plate_t/2+radius), mover.getY()); gameSurface.fill(255, 100, 100); gameSurface.rotateX(mover.getRotX()); gameSurface.rotateZ(mover.getRotZ()); gameSurface.shape(globe); gameSurface.popMatrix(); //Draw the text gameSurface.pushMatrix(); /*gameSurface.fill(0); gameSurface.textSize(15); gameSurface.text("Rotation X : "+f.format(rx/Math.PI*180)+"° Rotation Z : "+f.format(rz/Math.PI*180)+"° speed: "+f.format(speed/10), 5, 15); */ if (shiftMod) { gameSurface.textSize(50); gameSurface.text("[EDIT MOD ON]", 50, 500); } gameSurface.fill(255); gameSurface.popMatrix(); gameSurface.endDraw(); } + + void mouseWheel(MouseEvent event) { float e = event.getCount(); if (e<0) { speed-=speed<=2?0:1; } else { speed+=speed>=15?0:1; } } void mouseDragged() { - if (!shiftMod) { + if (!shiftMod && mousePermittedToMove) { float d=-(mX-mouseX)*speed/10; x_coord=x_coord+d<=0?0:x_coord+d>=width?width:x_coord+d; mX=mouseX; d=(mY-mouseY)*speed/10; y_coord=y_coord+d<=0?0:y_coord+d>=height?height:y_coord+d; mY=mouseY; } } void mousePressed() { + if(mouseY>height-botPartHeight){ + mousePermittedToMove=false; + }else{ + mousePermittedToMove=true; + } mX = mouseX; mY = mouseY; if (shiftMod && -plate_w/2 2*radius || Math.abs(yOnPlane-mover.location.y) > 2*radius)) { partSys=new ParticleSystem(new PVector(xOnPlane, yOnPlane), mover, radius); } } void keyPressed() { if (keyCode == SHIFT) { shiftMod=true; } } void keyReleased() { if (keyCode == SHIFT) { shiftMod=false; } } diff --git a/Project/Game/HScrollbar.pde b/Project/Game/HScrollbar.pde new file mode 100644 index 0000000..abb73aa --- /dev/null +++ b/Project/Game/HScrollbar.pde @@ -0,0 +1,98 @@ +class HScrollbar { + float barWidth; //Bar's width in pixels + float barHeight; //Bar's height in pixels + float xPosition; //Bar's x position in pixels + float yPosition; //Bar's y position in pixels + float sliderPosition, newSliderPosition; //Position of slider + float sliderPositionMin, sliderPositionMax; //Max and min values of slider + boolean mouseOver; //Is the mouse over the slider? + boolean locked; //Is the mouse clicking and dragging the slider now? + /** + * @brief Creates a new horizontal scrollbar + ** + @param x The x position of the top left corner of the bar in pixels + * @param y The y position of the top left corner of the bar in pixels + * @param w The width of the bar in pixels + * @param h The height of the bar in pixels + */ + HScrollbar (float x, float y, float w, float h) { + barWidth = w; + barHeight = h; + xPosition = x; + yPosition = y; + sliderPosition = xPosition + barWidth/2 - barHeight/2; + newSliderPosition = sliderPosition; + sliderPositionMin = xPosition; + sliderPositionMax = xPosition + barWidth - barHeight; + } + /** + * @brief Updates the state of the scrollbar according to the mouse movement + */ + void update() { + if (isMouseOver()) { + mouseOver = true; + } else { + mouseOver = false; + } + if (mousePressed && mouseOver) { + locked = true; + } + if (!mousePressed) { + locked = false; + } + if (locked) { + newSliderPosition = constrain(mouseX - barHeight/2, sliderPositionMin, sliderPositionMax); + } + if (abs(newSliderPosition - sliderPosition) > 1) { + sliderPosition = sliderPosition + (newSliderPosition - sliderPosition); + } + } + /** + * @brief Clamps the value into the interval + ** + @param val The value to be clamped + * @param minVal Smallest value possible + * @param maxVal Largest value possible + ** + @return val clamped into the interval [minVal, maxVal] + */ + float constrain(float val, float minVal, float maxVal) { + return min(max(val, minVal), maxVal); + } + /** + * @brief Gets whether the mouse is hovering the scrollbar + ** + @return Whether the mouse is hovering the scrollbar + */ + boolean isMouseOver() { + if (mouseX > xPosition && mouseX < xPosition+barWidth && + mouseY > yPosition && mouseY < yPosition+barHeight) { + return true; + } else { + return false; + } + } + /** + * @brief Draws the scrollbar in its current state + */ + void display() { + noStroke(); + fill(204); + rect(xPosition, yPosition, barWidth, barHeight); + if (mouseOver || locked) { + fill(0, 0, 0); + } else { + fill(102, 102, 102); + } + rect(sliderPosition, yPosition, barHeight, barHeight); + } + /** + * @brief Gets the slider position + ** + @return The slider position in the interval [0,1] + * corresponding to [leftmost position, rightmost position] + */ + float getPos() { + return (sliderPosition - xPosition)/(barWidth - barHeight); + } +} diff --git a/Project/Game/Mover.pde b/Project/Game/Mover.pde index a410de0..26ecada 100644 --- a/Project/Game/Mover.pde +++ b/Project/Game/Mover.pde @@ -1,79 +1,80 @@ class Mover { float normalForce = 1; float gravityConstant = 1; float mu = 0.10; float eps=0.92; float frictionMagnitude; PVector location= new PVector(0, 0); PVector velocity= new PVector(0, 0); PVector friction; PVector gravity=new PVector(0, 0); Cylinder cyl=new Cylinder(); float rotX=0; float rotZ=0; Mover() { } void update() { velocity.add(gravity); gravity.x = sin(rz) * gravityConstant; gravity.y = -sin(rx) * gravityConstant; frictionMagnitude = normalForce * mu; friction = velocity.copy(); friction.mult(-1); friction.normalize(); friction.mult(frictionMagnitude); velocity.add(friction); location.add(velocity); //rotX+=velocity.x/cyl.cylinderBaseSize; rotX+=velocity.x/cyl.cylinderBaseSize; //rotZ+=velocity.y/cyl.cylinderBaseSize; rotZ+=velocity.y/cyl.cylinderBaseSize; } float getX() { return location.x; } float getY() { return location.y; } float getRotX() { return -rotZ; } float getRotY() { return 0; } float getRotZ() { return rotX; } void checkEdges() { if (location.x > plate_w/2|| location.x < -plate_w/2) { velocity.x*=-eps; } if (location.y > plate_w/2 || location.y < -plate_w/2) { velocity.y*=-eps; } location.y=Math.max(-plate_w/2, Math.min(plate_w/2, location.y)); location.x=Math.max(-plate_w/2, Math.min(plate_w/2, location.x)); } private void checkCylinderCollision(ArrayList arr, float r) { ArrayList touchingSet=new ArrayList(); for (PVector p : arr) { if (dist(p.x, p.y, location.x, location.y)<=cyl.cylinderBaseSize+r) {//if too close - //score += velocity.copy().mag(); + lastScore = velocity.copy().mag() * scoreFactor; + updateScore(lastScore); touchingSet.add(p); PVector nRadius=location.copy().sub(p).normalize(); location=nRadius.copy().mult(cyl.cylinderBaseSize+r).add(p); velocity.sub(nRadius.mult(2*velocity.dot(nRadius))).mult(eps);//V2 = V1 − 2(V1 · n)n } } arr.removeAll(touchingSet); } } diff --git a/Project/Game/ParticleSystem.pde b/Project/Game/ParticleSystem.pde index 4a63ec6..b23c5fe 100644 --- a/Project/Game/ParticleSystem.pde +++ b/Project/Game/ParticleSystem.pde @@ -1,76 +1,80 @@ // A class to describe a group of Particles class ParticleSystem { ArrayList cylinders; Cylinder cyl = new Cylinder(); float cylinderRadius = cyl.cylinderBaseSize; float radius; PVector mainCyl; PVector origin; boolean shutdown=false; Mover m; ParticleSystem(PVector origin,Mover m,float radius) { this.m=m; this.radius=radius; this.origin = origin.copy(); cylinders = new ArrayList(); mainCyl=origin; cylinders.add(mainCyl); } void addParticle() { PVector center; if(!cylinders.contains(mainCyl)){ shutdown=true; } int numAttempts = shutdown?0:100; for (int i=0; i=0; --i) { PVector p=cylinders.get(i); cyl.draw(p.x, p.y, z, r); } if(cylinders.contains(mainCyl)){ gameSurface.pushMatrix(); gameSurface.translate(mainCyl.x,z - cyl.cylinderHeight, mainCyl.y); + float angle = atan2(mainCyl.y-m.location.y, mainCyl.x-m.location.y); + gameSurface.rotateY(-angle+PI/2); gameSurface.rotateX(PI); - gameSurface.shape(robot,0,0,150,150); + gameSurface.scale(120); + gameSurface.shape(robot,0,0); gameSurface.popMatrix(); } } } diff --git a/Project/Game/ToDO.txt b/Project/Game/ToDO.txt new file mode 100644 index 0000000..c366dc5 --- /dev/null +++ b/Project/Game/ToDO.txt @@ -0,0 +1 @@ +- Quand on tue le vilain on a gagné et on gagne beaucoup de points \ No newline at end of file