My overall goal is to create a 3 way 'pong' game. A triangle border will be used with 3 paddles moving along each of the 3 sides. A ball will be bouncing within this triangle and the paddles will be used to try and stop the ball hitting each side of the triangle. To start off, I am trying to get a ball bouncing within the boundaries of a triangle. I currently just have a bouncing ball. Can anyone suggest how to go forward with this?
float x = 100;
float y = 100;
float xspeed = 1;
float yspeed = 3.3;
void setup() {
size(500,500);
}
void draw() {
background(255);
fill(255,10);
rect(0,0,width,height);
x = x + xspeed;
y = y + yspeed;
if ((x > width) || (x < 0)) {
xspeed = xspeed * -1;
}
if ((y > height) || (y < 0)) {
yspeed = yspeed * -1;
}
fill(175);
ellipse(x,y,16,16);
}
You're going to have to change your collision detection code so it detects when the circle collides with the triangle boundary instead of the edges of the screen.
Define your triangle as three line segments, then you can focus on detecting collision between the circle and each line segment. Google is your friend here, but this question has a bunch of answers.
Then you'll probably want to reflect the point around the line so that the circle bounces at an angle based on the line segment. Again, google is your friend, but here is another question with a bunch of answers.
I recommend splitting your problem up into smaller steps and focusing on one at a time. First get a program working that just checks whether a circle collides with a line segment: try hard-coded points at first, then maybe use the cursor position, then work your way up to a bouncing ball.
Related
I've recently built a game of pong for a UNI assignment in Processing, but whenever the ball hits the top, bottom, side of the screen or the 'paddle' it only bounces back once half the ball is off the screen. I just want the edge of the ball to hit first rather than the centre, but am unsure where my code is going wrong. I hope this makes sense, I'm a definite beginner.
Here is my code for reference
//underwater pong
float x, y, speedX, speedY;
float diam = 10;
float rectSize = 200;
float diamHit;
PImage bg;
PImage img;
int z;
void setup() {
size(920, 500);
smooth();
fill(255);
stroke(255);
imageMode(CENTER);
bg = loadImage("underthesea.jpg");
img = loadImage("plastic.png");
reset();
}
void reset() {
x = width/2;
y = height/2;
//allows plastic to bounce
speedX = random(5, 5);
speedY = random(5, 5);
}
void draw() {
background(bg);
image(img, x/2, y);
rect(0, 0, 20, height);
rect(width/2, mouseY-rectSize/2, 50, rectSize);
//allows plastic to bounce
x += speedX;
y += speedY;
// if plastic hits movable bar, invert X direction
if ( x > width-30 && x < width -20 && y > mouseY-rectSize/2 && y < mouseY+rectSize/2 ) {
speedX = speedX * -1;
}
// if plastic hits wall, change direction of X
if (x < 25) {
speedX *= -1.1;
speedY *= 1.1;
x += speedX;
}
// if plastic hits up or down, change direction of Y
if ( y > height || y < 0 ) {
speedY *= -1;
}
}
void mousePressed() {
reset();
}
I wasn't able to run your code because I am missing the background and plastic images, but here's what's probably going wrong. I'm not 100% since I do not know the dimensions of your images either.
You are using imageMode(CENTER). See the documentation for details.
From the docs:
imageMode(CENTER) interprets the second and third parameters of image() as the image's center point. If two additional parameters are specified, they are used to set the image's width and height.
This treats the coordinates you input into the image function as the center of the image.
Your first issue is that you are placing your image at x/2 but doing all your collision checks with x in mind. x does not represent the middle of your image, because you're drawing it at x/2.
Then I'm not sure if you are doing your horizontal collision checks right, as you are checking against hardcoded values. I do know you are doing your vertical collision checks wrong. You are checking if the center of the image is at the top of the canvas, 0, or the bottom of the canvas, height. This means that your image will already have moved out of the screen halfway.
If you want to treat the image coordinates as the center of your image, you need to check the left edge of the image at x - imageWidth / 2, the right edge at X+ imageWidth / 2, the top edge at y - imageWidth / 2 (remember the y coordinates are 0 at the top of the canvas and increase towards the bottom) and the bottom at y - imageWidth / 2. Here's a great website which goes into more detail on 2d collision detection, i'd highly recommend you give it a read. It's an awesome website.
EDIT I changed my code to the one below but it still clips through the terrain sometimes. It also likes to bounce on corners where it should slide off.
I tried implementing my own collision for a little game I am making. This is the code for my ball class.
class Ball {
//config
float gforce = 1;
float friction = 0.8;
float elasticity = 0.5;
//vars
PVector position;
PVector velocity = new PVector(0, 0);
Ball(PVector p) {position = p;}
void render(){fill(255, 255, 0); noStroke(); circle(position.x, height-position.y, 16);}
PVector bounce(PVector v, PVector n){ //calculate velocity after bounce
PVector u = PVector.mult(n,v.dot(n)/n.dot(n));
PVector w = PVector.sub(v, u);
return PVector.sub(PVector.mult(w, friction), PVector.mult(u, elasticity));
}
Boolean intersect(PVector c, PVector p1, PVector p2, float r){
if (c.dist(p1)<=r || c.dist(p2)<=r){return true;}
float len = p1.dist(p2);
float dot = ( ((c.x-p1.x)*(p2.x-p1.x)) + ((c.y-p1.y)*(p2.y-p1.y)) )/(len*len);
PVector closest = PVector.add(p1, PVector.mult(PVector.sub(p2, p1), dot));
float d1 = closest.dist(p1);
float d2 = closest.dist(p2);
if (d1+d2>=len-0.1 && d1+d2<=len+0.1){if(closest.dist(c)<=r){return true;}}
return false;
}
Boolean intersect(PVector p1, PVector p2, PVector p3, PVector p4){ //if the line p1p2 intersects line p3p4
float uA = ((p4.x-p3.x)*(p1.y-p3.y) - (p4.y-p3.y)*(p1.x-p3.x))/((p4.y-p3.y)*(p2.x-p1.x) - (p4.x-p3.x)*(p2.y-p1.y));
float uB = ((p2.x-p1.x)*(p1.y-p3.y) - (p2.y-p1.y)*(p1.x-p3.x))/((p4.y-p3.y)*(p2.x-p1.x) - (p4.x-p3.x)*(p2.y-p1.y));
if (uA>=0 && uA<=1 && uB>=0 && uB<=1){return true;}else{return false;}
}
void moveBall(){
//if the ball is stationary
if(velocity.mag()==0 || velocity.mag()==1 || velocity.mag()==2){
PVector v = new PVector(min(280, max(-280, mX-mouseX)), min(280, max(-280, mouseY-mY))).div(28);
int n = int(max(abs(v.x), abs(v.y)));
v.normalize().mult(25);
//render arrow
if(mX!=0 && mousePressed){
strokeWeight(5); stroke(#75D5FD); line(mX, mY, mouseX, mouseY);
for (int i=0; i<n+1; i++){
noStroke(); fill(510*(float)i/12, 510*(1-(float)i/12), 0, 55+200*(1-(float)i/12)); circle(mX+(v.x*i), mY-(v.y*i), 15);
}
}
if(mX==0 && mouseP){mX=mouseX; mY=mouseY; mouseP=false;}
if(mX!=0 && mouseR){b.velocity = new PVector(min(280, max(-280, mX-mouseX)), min(280, max(-280, mouseY-mY))).div(8); mX=0; mouseR=false;} //apply velocity
}else {
//if the ball is still, do not allow additional movement
if(mX!=0 && mousePressed){stroke(200); line(mX, mY, mouseX, mouseY);}
if(mX==0 && mouseP){mX=mouseX; mY=mouseY; mouseP=false;}
if(mX!=0 && mouseR){mX=0; mouseR=false;}
}
}
void collision(){
//test collision with terrain
for (int i=1; i<l.points.length; i++){
PVector centerout = PVector.div(velocity, velocity.mag()).mult(8);
strokeWeight(5); stroke(255,0,0); line(position.x, height-position.y, position.x+centerout.x+velocity.x, height-position.y-centerout.y-velocity.y);
if(intersect(position, l.points[i-1], l.points[i], 7) || intersect(position, PVector.add(position,velocity,centerout), l.points[i-1], l.points[i])){
velocity = bounce(velocity, l.normals[i-1]);
}
}
}
void move() {
moveBall();
collision();
position.add(velocity);
if(velocity.y>-10){velocity.y-=gforce;}
if(velocity.mag()<0.5){velocity.x=0; velocity.y=0;}
}
}
There is another object, l (which is the terrain), and it stores the array points[] which contains all the coordinates for the terrain. There is a line connecting each point and the ball detects if its velocity vector intersects that line, or if the ball itself intersects that line.
When i drag and release my mouse, it changes the velocity to whatever vector the mouse was dragged in. It then detects for a collision and it changes the velocity based on its current velocity and the normal of the terrain. However, when it does that, it flies in the opposite direction and flings itself through the floor.
How can I fix my code so that the collision and bouncing works as expected? Additionally it will be nice if the velocity eventually became 0 after bouncing a while.
Oh and usually the y position goes up the further you go down but I changed it so that the y position goes down the further you go down. SO at the very bottom, the y position is 0 instead of the height of the canvas.
Regarding your current problem, I actually have 2 different suggestions I decided to offer only the first one after consideration, as the second one can throw you into recursive madness, but I'm willing to expand on it if needed. I cannot really test them or offer you real code without a working example, though, so I'll stay on pseudocode level.
Solution 1: easy and robust
This one is pretty straightforward: save the pre-bounce vector. When the ball actually bounce, if the rebound has clipping, forget about it and inverse the initial vector. Since the ball came from this exact direction, you can be pretty damn sure that it can go back that way.
The algo would go like this:
if ValidateMove(CurrentVector):
Move(CurrentVector)
else:
NewVector = CalculateNewVector
if ValidateMove(NewVector):
Move(NewVector)
else:
Move(CurrentVector.Invert)
There's a possible problematic edge case, though. If another object is in movement, it may block the ball's inverted path. Honestly, I wouldn't care about the chance that this happens unless your game is specifically prone to this.
Hope this'll help!
I'm using processing, and I'm trying to create a circle from the pixels i have on my display.
I managed to pull the pixels on screen and create a growing circle from them.
However i'm looking for something much more sophisticated, I want to make it seem as if the pixels on the display are moving from their current location and forming a turning circle or something like this.
This is what i have for now:
int c = 0;
int radius = 30;
allPixels = removeBlackP();
void draw {
loadPixels();
for (int alpha = 0; alpha < 360; alpha++)
{
float xf = 350 + radius*cos(alpha);
float yf = 350 + radius*sin(alpha);
int x = (int) xf;
int y = (int) yf;
if (radius > 200) {radius =30;break;}
if (c> allPixels.length) {c= 0;}
pixels[y*700 +x] = allPixels[c];
updatePixels();
}
radius++;
c++;
}
the function removeBlackP return an array with all the pixels except for the black ones.
This code works for me. There is an issue that the circle only has the numbers as int so it seems like some pixels inside the circle won't fill, i can live with that. I'm looking for something a bit more complex like I explained.
Thanks!
Fill all pixels of scanlines belonging to the circle. Using this approach, you will paint all places inside the circle. For every line calculate start coordinate (end one is symmetric). Pseudocode:
for y = center_y - radius; y <= center_y + radius; y++
dx = Sqrt(radius * radius - y * y)
for x = center_x - dx; x <= center_x + dx; x++
fill a[y, x]
When you find places for all pixels, you can make correlation between initial pixels places and calculated ones and move them step-by-step.
For example, if initial coordinates relative to center point for k-th pixel are (x0, y0) and final coordinates are (x1,y1), and you want to make M steps, moving pixel by spiral, calculate intermediate coordinates:
calc values once:
r0 = Sqrt(x0*x0 + y0*y0) //Math.Hypot if available
r1 = Sqrt(x1*x1 + y1*y1)
fi0 = Math.Atan2(y0, x0)
fi1 = Math.Atan2(y1, x1)
if fi1 < fi0 then
fi1 = fi1 + 2 * Pi;
for i = 1; i <=M ; i++
x = (r0 + i / M * (r1 - r0)) * Cos(fi0 + i / M * (fi1 - fi0))
y = (r0 + i / M * (r1 - r0)) * Sin(fi0 + i / M * (fi1 - fi0))
shift by center coordinates
The way you go about drawing circles in Processing looks a little convoluted.
The simplest way is to use the ellipse() function, no pixels involved though:
If you do need to draw an ellipse and use pixels, you can make use of PGraphics which is similar to using a separate buffer/"layer" to draw into using Processing drawing commands but it also has pixels[] you can access.
Let's say you want to draw a low-res pixel circle circle, you can create a small PGraphics, disable smoothing, draw the circle, then render the circle at a higher resolution. The only catch is these drawing commands must be placed within beginDraw()/endDraw() calls:
PGraphics buffer;
void setup(){
//disable sketch's aliasing
noSmooth();
buffer = createGraphics(25,25);
buffer.beginDraw();
//disable buffer's aliasing
buffer.noSmooth();
buffer.noFill();
buffer.stroke(255);
buffer.endDraw();
}
void draw(){
background(255);
//draw small circle
float circleSize = map(sin(frameCount * .01),-1.0,1.0,0.0,20.0);
buffer.beginDraw();
buffer.background(0);
buffer.ellipse(buffer.width / 2,buffer.height / 2, circleSize,circleSize);
buffer.endDraw();
//render small circle at higher resolution (blocky - no aliasing)
image(buffer,0,0,width,height);
}
If you want to manually draw a circle using pixels[] you are on the right using the polar to cartesian conversion formula (x = cos(angle) * radius, y = sin(angle) * radius).Even though it's focusing on drawing a radial gradient, you can find an example of drawing a circle(a lot actually) using pixels in this answer
I'm currently working on a Processing sketch featuring a very basic gravity simulation (based on an example given in Daniel Schiffman's book Learning Processing) but my gravity keeps behaving in a bizarre way and I'm at a loss to know what to do about it. Here's the simplest example I can come up with:
float x = 50;
float y = 50;
float speed = 2;
float gravity = 0.1;
void setup() {
size(400, 400);
}
void draw() {
background(255);
fill(175);
stroke(0);
ellipseMode(CENTER);
ellipse(x, y, 10, 10);
y = y + speed;
speed = speed + gravity;
//Dampening bounce effect when the ball hits bottom
if (y > height) {
speed = speed * -0.95;
}
}
The above is virtually identical to what's in Schiffman's book aside from a different starting speed and a different window size. It seems to work fine for the first two bounces but on the third bounce the ball becomes stuck to the bottom of the window.
I have no idea where to even begin trying to debug this. Can anyone give any pointers?
If y remains greater than height, your code just keeps flipping the speed over and over without giving the ball a chance to bounce. You want the ball to move away from the boundary whenever it is at or past the boundary.
Setting y to height in the (if y > height) block helps, but the ball never comes to 'rest' (sit on the bottom line when done).
There are two problems with Shiffman's example 5.9 which is where you probably started:
1) y can become greater than (height + speed), which makes it seem to go thud on the ground or bounce wildly
-- fixed with the suggestion
2) The algorithm to update y does things in the wrong order, so the ball never comes to rest
Correct order is:
if it is time to 'bounce
negate speed including dampening (* 0.95)
set y to location of the 'ground'
Add gravity to speed
Add speed to y
This way, when it is time to bounce (y is > height), and speed is positive (going downward):
speed is set to negative (times dampening)
y is set to height
speed is set less negative by the gravity factor
y is made less positive by adding the (still negative) speed
In my example, I didn't want the object to disappear so I use a variable named 'limit' to be the lowest (most positive y) location for the center of the object.
float x = 50;
float y = 50;
float speed = 2;
float gravity = 0.1;
void setup() {
size(400, 400);
y = height - 20;
}
void draw() {
background(255);
fill(175);
stroke(0);
ellipseMode(CENTER);
ellipse(x, y, 10, 10);
float limit = height - (speed + 5);
//Dampening bounce effect when the ball hits bottom
if (y > limit) {
speed = speed * -0.95;
y = limit;
}
speed = speed + gravity;
y = y + speed;
}
I need some help understanding the basics of a frustum transformation. Mainly, how depth works.
The following uses a viewport of 768x1024. Using an Orthogonal projection and a square of 768x768 (z defaults to 0) with no translation or scaling, and a viewport of glViewport(0, 0, 768, 1024) this square easily fills the width of the frame:
Now when I change the project to a frustum and mess with the z translation, the square scales appropriately due to the perspective changes.
Here is the same square in such an environment:
I can play with this z translation, as well as the near and far parameters of the frustum matrix and make the square change is apparent onscreen size accordingly. Fine.
But what I cannot figure out is the obvious relationship between its onscreen size and these depth parameters.
For example, suppose I want to use a frustum but have the square fill the frame width, as in my first example image above. How to achieve this?
I would think that if the z translation matched the near plane, then you'd essentially have a square "right in front of the camera", filling the frame. But I cannot figure a way to achieve this. If my near is 1 and my z translation is -1, then the square should be sitting right on the near plane itself (right?!) , filling the width of the frame (where the frustum's left and right planes are the same as the orthogonal projection).
I could paste a bunch of code here to show what I'm doing but I think the concept here is clear. I just want to figure out where the near plane actually is, how to situate something on it, as this will help me understand how the frustum is working.
Okay here is the relevant code I'm using, where width=768 and height=1024.
My vertex shader is the simple gl_Position=Projection*Modelview*Position;
My projection matrix (frustum) is thus:
Frustum(-width/2, width/2, -height/2, height/2, 1,10);
This function is:
static Matrix4<T> Frustum(T left, T right, T bottom, T top, T near, T far)
{
T a = 2 * near / (right - left);
T b = 2 * near / (top - bottom);
T c = (right + left) / (right - left);
T d = (top + bottom) / (top - bottom);
T e = - (far + near) / (far - near);
T f = -2 * far * near / (far - near);
Matrix4 m;
m.x.x = a; m.x.y = 0; m.x.z = 0; m.x.w = 0;
m.y.x = 0; m.y.y = b; m.y.z = 0; m.y.w = 0;
m.z.x = c; m.z.y = d; m.z.z = e; m.z.w = -1;
m.w.x = 0; m.w.y = 0; m.w.z = f; m.w.w = 1;
return m;
}
My square is just two 2d triangles with a default z=0, and an x range from left as -768/2 and right edge at 768/2. The square is clearly working properly as my first image above shows, using the orthogonal projection. (Though I switched to the frustum projection for this question)
To draw the square, I translate the Modelview with:
Translate(0, 0, -1);
Using:
static Matrix4<T> Translate(T x, T y, T z)
{
Matrix4 m;
m.x.x = 1; m.x.y = 0; m.x.z = 0; m.x.w = 0;
m.y.x = 0; m.y.y = 1; m.y.z = 0; m.y.w = 0;
m.z.x = 0; m.z.y = 0; m.z.z = 1; m.z.w = 0;
m.w.x = x; m.w.y = y; m.w.z = z; m.w.w = 1;
return m;
}
As you can see, the translation should put the square on the near plane, yet it looks like this:
If I translate instead of -1.01 just to be sure I avoid near clipping, the result is the same. If I do not translate, thus z=0, the square does not appear, as you'd expect, since it would be behind the camera.
In your frustum matrix, m.w.w should be 0, not 1. This will fix your problem.
But, the mistake isn't your fault. It's my fault! I'm actually the one who wrote that code in the first place, and unfortunately it has proliferated. It's an errata in my book (iPhone 3D Programming), which is where it first appeared.
Feeling very guilty about this!
If my near is 1 and my z translation is -1, then the square should be sitting right on the near plane itself (right?!)
Yes
, filling the width of the frame (where the frustum's left and right planes are the same as the orthogonal projection).
Not neccesarily. The near plane has the extents given with the left, right, bottom and top parameters of glFrustum. A rectangle going to exactly those bounds will snugly fit the viewport when being placed at the near plane distance.