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!
Related
I can't figure it out, how do I move the Player slowly, scaleable with a speed value to the point, where the touch happens?
My Camera is attached to the player.
You should use Vector2.Lerp, Input.GetTouch, and Touch.Position. Here is a code example that prints the current touch position:
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
{
Debug.Log(Input.GetTouch(0).position);
}
Now, we should add this to a certain position in the world. We can use Camera.ScreenPointToRay for this part:
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
{
Ray ray = Camera.main.ScreenPointToRay(Input.GetTouch(0).position);
RaycastHit2D hit;
Physics2D.Raycast(ray, out hit);
Debug.Log(hit.point);
}
Here, we actually get the position in the world that you have pressed. We can then use this along with Lerp to move an object to the position:
public float scaleFactor = 0.2f; //how fast to move object.
public GameObject moving // drag the object you want to move in this slot.
public Vector2 Lerped;//Vector3 if you’re working in 3D.
public float time = 0f;
public bool lerping = false;
private Vector2 newPos = new Vector2();
//Vector3 if you’re working in 3D.
RaycastHit2D hit;//remove the 2D after RaycastHit if you are in 3D. If you are in 2D, leave it.
float dist;
...
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
{
Ray ray = Camera.main.ScreenPointToRay(Input.GetTouch(0).position);
Physics2D.Raycast(ray, out hit);//same with this one. Remove the 2D if you are in 3D.
Debug.Log(hit.point);
dist = Vector2.Distance(moving.transform.position, hit.point);
//Vector3 if you’re in 3D.
lerping = true;
}
if (lerping)
{
time += Time.deltaTime * dist * scaleFactor;
lerp(hit.point.x, hit.point.y, hit.point.z);
//remove hit.point.z if you are in 2D.
}
else
{
time = 0f;
}
if (moving.transform.position == new Vector3(hit.point.x, hit.point.y, hit.point.z);
//remove hit.point.z if in 2D, and switch Vector3 to Vector2 if in 2D.
{
lerping = false;
}
moving.transform.position = newPos;
And then in another part of the script:
public void lerp(float x, float y, float z)
//remove the float z and the comma before it if you are in unity 2d.
{
Vector3 pos = new Vector3(x, y, z);
//Vector2 and no z if you’re in 2D.
newPos = Vector2.Lerp(moveObj, pos, time);
//Vector3 if in 3D.
}
This is a pretty large script, and it’s pretty hard to explain, so I will say this: do what the comments tell you, and tell me if you get any errors. I haven’t actually tested the script, so if anyone notices them, please tell me.
Now, what the script does: basically, it gets the point of where you touched, and makes the player slowly go towards it (the speed changes by the distance and scaleFactor variable of which you can change.)
//declaringglobalvaribales
float dia1=50;
//diameteroftheface
float x=400;
float y=400;
float speedX=4;
float speedY=0;
//setup
void setup() {
size(810, 810);
} //draw
void draw() {
background(225);
fill(225, 0, 0);
stroke(0);
ellipse(x, y, dia1, dia1);
fill(0, 225, 0);
//a nose
triangle(x, y, x+10, y+10, x-10, y+10);
//movingtheballXway
x=x+speedX;
//movingtheballYway
y=y+speedY;
//if it hits the left or right corners it will turn around
if (x>width-dia1/2 || x<2+dia1/2) {
speedX=speedX*-1;
}
// it its hits the top or bottom it will turn around
if (y>height-dia1/2 || y<2+dia1/2) {
speedY=speedY*-1;
}
// this code to move it according to the keys W S D A
void keyPressed() {
if (keyCode=='W') {
speedX=0;
speedY=-4;
}
if (keyCode=='S') {
speedX=0;
speedY=4;
}
if (keyCode=='A') {
speedX=-4;
speedY=0;
}
if (keyCode=='D') {
speedX=4;
speedY=0;
}
}
I made this ball with a nose, which moves around the screen with the keys W S D A. If it hits the edges it will bounce back.
I'm trying to change the direction the ball is facing to make it face the same way as it's moving. I wanted to use rotate for this, but once I use rotate it throws all the coordinates off. Rotating it back doesn't help either.
I have commented out the stuff I have tried to do. For example, I tried to translate it to 250,250 and then rotate it afterwards, but then the X and Y coordinates are switched. Also, the ball won't go all the way to the corners and it moves out (since it's translated down).
What kind of rotation/translation logic do I need to change?
It is likely that using the rotate function on your triangle is wreaking havoc as you are performing the rotate on several variables in your draw loop because you're not telling processing exactly which object you want to transform. One way to do this is to look up the pushMatrix and popMatrix functions (google "processing pushMatrix" to see helpful info for how to use the type and associated functions). It would be cumbersome to implement this into your code as the triangle is created in your draw loop every frame. An easy way to make transformations to a specific shape you have (in your instance, a triangle) is to store it as a PShape and then make transformations as you need to. Since PShape's can easily be transformed using PShape functions you don't need to worry about your transformations effecting other variables (so no need to use push/popmatrix. Here is a commented version of your code that implements your Triangle as a PShape.
//declaringglobalvaribales
float dia1=50;
//diameteroftheface
float x=400;
float y=400;
float speedX=4;
float speedY=0;
//Initialize PShape which we can later store a triangle in
PShape tri;
void setup() {
size(810, 810);
//Initialize triangle - this triangle faces right
tri = createShape(TRIANGLE, 0, 10, 0, -10, 10, 0);
}
void draw() {
background(225);
fill(225, 0, 0);
stroke(0);
ellipse(x, y, dia1, dia1);
fill(0, 225, 0);
//Draw the stored PShape at x and y coordinate
shape(tri,x,y);
//movingtheballXway
x=x+speedX;
//movingtheballYway
y=y+speedY;
//if it hits the left or right corners it will turn around
if (x>width-dia1/2 || x<2+dia1/2) {
speedX=speedX*-1;
//Flip PShape rotation
tri.rotate(PI);
} // it its hits the top or bottom it will turn around
if (y>height-dia1/2 || y<2+dia1/2) {
speedY=speedY*-1;
//Flip PShape rotation
tri.rotate(PI);
}
}
// this code to move it according to the keys W S D A
void keyPressed() {
if (keyCode=='W') {
speedX=0;
speedY=-4;
//reset triangle orientation (transformation matrix) to original then rotate to face UP
tri.resetMatrix();
tri.rotate(-PI/2);
}
if (keyCode=='S') {
//reset triangle orientation (transformation matrix) to original then rotate to face DOWN
speedX=0;
speedY=4;
tri.resetMatrix();
tri.rotate(PI/2);
}
if (keyCode=='A') {
//reset triangle orientation (transformation matrix) to original then rotate to face LEFT
tri.resetMatrix();
tri.rotate(PI);
speedX=-4;
speedY=0;
}
if (keyCode=='D') {
//reset triangle orientation (transformation matrix) to original - it is already pointing right
tri.resetMatrix();
speedX=4;
speedY=0;
}
}
I suspect your next step, or a more efficient way to write this piece of code might be to begin to implement PVectors (google processing PVectors to see helpful info for how to use the type and associated functions) for position and direction of your 'ball'. Here is some commented code that begins to show you how you might implement this in your current code. Although there are many improvements that can be made on this. For more information on how vectors work in processing follow this tutorial - http://natureofcode.com/book/chapter-1-vectors/
//declaringglobalvaribales
//diameteroftheface
float dia1=50;
//initialize position PVector and tell it where you want it to be - in this case 400,400
PVector position = new PVector(400, 400);
//how many steps you want your position to move per frame
float speed=4;
//initialize direction vector as 0,0 - the ellipse will not move until you give it a
//direction as it is initialized with no direction
PVector direction = new PVector(0, 0);
void setup() {
size(810, 810);
}
void draw() {
background(225);
fill(225, 0, 0);
stroke(0);
//draw ellipse at your position PVector using the PVectors x and y values
ellipse(position.x, position.y, dia1, dia1);
fill(0, 225, 0);
//drawing a line to indicate what direction the ellipse is heading in using the position coordinates and the position plus direction
line(position.x, position.y, position.x+direction.x*4, position.y+direction.y*4);
// add the direction to the position to make it move
position =position.add(direction);
//if the position PVector is close to sketch edges invert its direction by multiplying direction PVector by -1
if (position.x>width-dia1/2 || position.x<2+dia1/2) {
direction.mult(-1);
}
if (position.y>height-dia1/2 || position.y<2+dia1/2) {
direction.mult(-1);
}
}
// this code to move it according to the keys W S D A
void keyPressed() {
//set the direction coordinates based on keypresses
//also multiply the direction by speed variable so it moves at a speed set at top of script
if (keyCode=='W') {
direction.y = -1*speed;
direction.x = 0;
}
if (keyCode=='S') {
direction.y = 1*speed;
direction.x = 0;
}
if (keyCode=='A') {
direction.x = -1*speed;
direction.y = 0;
}
if (keyCode=='D') {
direction.x = 1*speed;
direction.y = 0;
}
}
If you have a center point, an angle you want to face, and a distance from that center, you can use cos() and sin() to calculate the end point. Here's a simple example:
float angle = 0;
float distance = 25;
void draw(){
angle += .01;
float startX = width/2;
float startY = height/2;
float endX = startX + cos(angle)*distance;
float endY = startY + sin(angle)*distance;
background(255);
line(startX, startY, endX, endY);
}
In the future, please try to narrow your question down to an MCVE like this before posting.
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.
I want to achieve a slow fade in size on every collapse into itself. In other words, when the circle is at its biggest, the ellipses will be at the largest in size and conversely the opposite for the retraction. So far I am trying to achieve this affect by remapping the cSize from the distance of the center point, but somewhere along the way something is going wrong. At the moment I am getting a slow transition from small to large in ellipse size, but the inner ellipses are noticeably larger. I want an equal distribution of size amongst all ellipses in relation to center point distance.
I've simplified the code down to 4 ellipses rather than an array of rows of ellipses in order to hopefully simplify this example. This is done in the for (int x = -50; x <= 50; x+=100).
I've seen one or two examples that slightly does what I want, but is more or less static. This example is kind of similar because the ellipse size gets smaller or larger in relation to the mouse position
Distance2D
Here is an additional diagram of the grid of ellipses I am trying to create, In addition, I am trying to scale that "square grid" of ellipses by a center point.
Multiple ellipses + Scale by center
Any pointers?
float cSize;
float shrinkOrGrow;
void setup() {
size(640, 640);
noStroke();
smooth();
fill(255);
}
void draw() {
background(#202020);
translate(width/2, height/2);
if (cSize > 10) {
shrinkOrGrow = 0;
} else if (cSize < 1 ) {
shrinkOrGrow = 1;
}
if (shrinkOrGrow == 1) {
cSize += .1;
} else if (shrinkOrGrow == 0) {
cSize -= .1;
}
for (int x = -50; x <= 50; x+=100) {
for (int y = -50; y <= 50; y+=100) {
float d = dist(x, y, 0, 0);
float fromCenter = map(cSize, 0, d, 1, 10);
pushMatrix();
translate(x, y);
rotate(radians(d + frameCount));
ellipse(x, y, fromCenter, fromCenter);
popMatrix();
}
}
}
The values you're passing into the map() function don't make a lot of sense to me:
float fromCenter = map(cSize, 0, d, 1, 100);
The cSize variable bounces from 1 to 10 independent of anything else. The d variable is the distance of each ellipse to the center of the circle, but that's going to be static for each one since you're using the rotate() function to "move" the circle, which never actually moves. That's based only on the frameCount variable, which you never use to calculate the size of your ellipses.
In other words, the position of the ellipses and their size are completely unrelated in your code.
You need to refactor your code so that the size is based on the distance. I see two main options for doing this:
Option 1: Right now you're moving the circles on screen using the translate() and rotate() functions. You could think of this as the camera moving, not the ellipses moving. So if you want to base the size of the ellipse on its distance from some point, you have to get the distance of the transformed point, not the original point.
Luckily, Processing gives you the screenX() and screenY() functions for figuring out where a point will be after you transform it.
Here's an example of how you might use it:
for (int x = -50; x <= 50; x+=100) {
for (int y = -50; y <= 50; y+=100) {
pushMatrix();
//transform the point
//in other words, move the camera
translate(x, y);
rotate(radians(frameCount));
//get the position of the transformed point on the screen
float screenX = screenX(x, y);
float screenY = screenY(x, y);
//get the distance of that position from the center
float distanceFromCenter = dist(screenX, screenY, width/2, height/2);
//use that distance to create a diameter
float diameter = 141 - distanceFromCenter;
//draw the ellipse using that diameter
ellipse(x, y, diameter, diameter);
popMatrix();
}
}
Option 2: Stop using translate() and rotate(), and use the positions of the ellipses directly.
You might create a class that encapsulates everything you need to move and draw an ellipse. Then just create instances of that class and iterate over them. You'd need some basic trig to figure out the positions, but you could then use them directly.
Here's a little example of doing it that way:
ArrayList<RotatingEllipse> ellipses = new ArrayList<RotatingEllipse>();
void setup() {
size(500, 500);
ellipses.add(new RotatingEllipse(width*.25, height*.25));
ellipses.add(new RotatingEllipse(width*.75, height*.25));
ellipses.add(new RotatingEllipse(width*.75, height*.75));
ellipses.add(new RotatingEllipse(width*.25, height*.75));
}
void draw() {
background(0);
for (RotatingEllipse e : ellipses) {
e.stepAndDraw();
}
}
void mouseClicked() {
ellipses.add(new RotatingEllipse(mouseX, mouseY));
}
void mouseDragged() {
ellipses.add(new RotatingEllipse(mouseX, mouseY));
}
class RotatingEllipse {
float rotateAroundX;
float rotateAroundY;
float distanceFromRotatingPoint;
float angle;
public RotatingEllipse(float startX, float startY) {
rotateAroundX = (width/2 + startX)/2;
rotateAroundY = (height/2 + startY)/2;
distanceFromRotatingPoint = dist(startX, startY, rotateAroundX, rotateAroundY);
angle = atan2(startY-height/2, startX-width/2);
}
public void stepAndDraw() {
angle += PI/64;
float x = rotateAroundX + cos(angle)*distanceFromRotatingPoint;
float y = rotateAroundY + sin(angle)*distanceFromRotatingPoint;
float distance = dist(x, y, width/2, height/2);
float diameter = 50*(500-distance)/500;
ellipse(x, y, diameter, diameter);
}
}
Try clicking or dragging in this example. User interaction makes more sense to me using this approach, but which option you choose really depends on what fits inside your head the best.
So I’m trying to give an obj some physics, and have work with the example of the bouncing ball that processing comes with, and I have make it instead of a ball to be a cylinder, but I can’t find the way to make the obj to have the physics.
I’m importing the obj with the library saito.objloader. I have this code, which is the invocation of the obj
import processing.opengl.*;
import saito.objloader.*;
OBJModel modelCan;
PVector location; // Location of shape
PVector velocity; // Velocity of shape
PVector gravity; // Gravity acts at the shape's acceleration
float rotX, rotY;
void setup() {
size(1028, 768, OPENGL);
frameRate(30);
modelCan = new OBJModel(this, "can.obj", "absolute", TRIANGLES);
modelCan.scale(50);
modelCan.translateToCenter();
modelCan.enableTexture();
location = new PVector(100,100,100);
velocity = new PVector(1.5,2.1,3);
gravity = new PVector(0,0.2,0);
}
void draw() {
background(129);
lights();
// Add velocity to the location.
location.add(velocity);
// Add gravity to velocity
velocity.add(gravity);
// Bounce off edges
if ((location.x > width) || (location.x < 0)) {
velocity.x = velocity.x * -1;
}
if (location.y > height) {
// We're reducing velocity ever so slightly
// when it hits the bottom of the window
velocity.y = velocity.y * -0.95;
location.y = height;
}
if (location.z < -height || location.z > 0) { //note that Zaxis goes 'into' the screen
velocity.z = velocity.z * -0.95;
//location.z = height;
}
println("location x: "+location.x);
println("location y: "+location.y);
println("location z: "+location.z);
println("velocidad x: "+velocity.x);
println("velocidad y: "+velocity.y);
println("velocidad z: "+velocity.z);
pushMatrix();
translate(width/2, height/2, 0);
modelCan.draw();
popMatrix();
}
I hope you guys can help me! Thanks!
The bouncing ball example is a very simple example, and I guess you can't apply it to a cylinder in an easy way.
You might want to have a look at these sites :
http://www.wildbunny.co.uk/blog/2011/04/20/collision-detection-for-dummies/
http://shiffman.net/ (his book is essential )
https://github.com/shiffman/Box2D-for-Processing (again : Daniel Shiffman)
Collision detection is made in computer time : it means that it's correlated with the frequency of your program execution. For instance, the velocity of an object can be so big that between two render cycles your first object is passing through your second object: collision detection won't happen. They didn't collide during the previous cycle, and don't collide during the current cycle.
That's why some physics "engine" like Box2D try to anticipate the collision, computing the bounding box of each object.
If you want to make accurate collision detection in 3D, you can look at the Bullet library, used in many games and movies : http://bulletphysics.org/wordpress/ but the learning curve might be large.
In your program, you can try to give a threshold to your collision detection (and add the dimensions of your object to its position, if you want the whole body to collide, not only its center !): if your object's position is between your limit and (your limit +/- a threshold) consider it's colliding, but you won't totally avoid tunnelling.