Currently I'm working on a simple 2d platformer, and I decided to work on physics before working on the general concept of the game. I actually haven't learned physics in school or anything, so I'm just using google/youtube tutorials as my main resources. I've currently gotten jumping working pretty nicely, but moving side to side isn't what I would like it to be. I want it to use acceleration/deceleration vs. just incrementing/decrementing the x position by a constant speed. Now I've tried using this website for x motion but that seems to be what I'm using for jumping. Here is my current player class:
import game.Game;
import game.input.PlayerController;
public class Player {
private float dt = 0.18F, gravity = 9.81F;
public float x, y, dx, dy;
private PlayerController controller;
private boolean jumping, onGround;
public float speed = 7.5F;
public float jh = 60F;
public float vy = 120F;
public Player() {
controller = new PlayerController();
}
public void update() {
controller.update(this);
dy += gravity * dt * (jumping ? -1F : 1F);
if(!jumping && !onGround && dy > vy) dy = vy;
y += dy * dt + 0.5F * gravity * dt * dt;
x += dx;
if (y > Game.height - 32) {
dy = 0;
y = Game.height - 32;
onGround = true;
} else
onGround = false;
if(dy < jh) { jumping = false;}
dx = 0;
}
public void render(Graphics g) {
g.setColor(Color.red);
g.fillRect(x, y, 32, 32);
}
public void moveLeft() {
dx -= speed;
}
public void moveRight() {
dx += speed;
}
public void jump() {
if (onGround) {
jumping = true;
dy -=jh;
}
}
}
In the PlayerController class I'm just calling player.moveLeft() and player.moveRight() when the left and right keys are pressed.
If anybody has a good idea on how to make smoother movement, that would be very helpful. Thanks!
Related
I'm trying to create a simple Java program that takes a math function as input and displays a 2D (x and y) graph in a JPanel. I've got the graph displaying correctly initially.
I tried adding scrolling to the canvas (I called the JPanel subclass I made a GraphCanvas), by which I mean that when the mouse is dragged across it, the 2D plane scrolls as well and the graph is updated for the newly revealed areas.
To display this actual function, I am using a Path2D object that is made up of segments connecting enough points so that the graph is in maximum resolution, and I've been using AffineTransforms to translate the path.
It's worth noting that I've only tested the program so far with functions in the form f(x), and not parametrics, even though I've created a class and supporting code in the GraphedInstance class for parametric functions.
I didn't want to include all of the code for the project, but I pushed it to a GitHub repository here (I have very little knowledge of how GitHub works but that should be right).
My problem is that at the moment, whenever I try to scroll the graph, the function just kind of does its own thing and doesn't connect correctly with the initialized piece. It isn't displaying the newly generated parts correctly.
I've done some work on it and improved the problem significantly by waiting to transform the path describing the function until after I've added the new pieces to it, but it still doesn't connect correctly.
This is the method that is supposed to create and stitch together the new parts of the function path, and it's where I think the issue probably lies. This and the rest of the project can also be found in the GitHub repository. Also, sorry for any messy code and/or lack of comments, I'm only a freshman in college and haven't really had any formal training in Java, I've just kind of picked it up as I go along. I would be happy to take any suggestions or recommendations of better ways to do anything as well!
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
public class Minimal {
private static int[] lastPointer = new int[2];
private static double xMin, xMax, yMin, yMax;
private static double xScale, yScale;
private static double minXGraphed, maxXGraphed;
public static Path2D.Double function;
//The frame enclosing the graphing canvas
private static JFrame frame;
//The graphing canvas
private static JPanel canvas = new JPanel() {
private static final int DEFAULT_CANVAS_SIZE = 600;
//Ensures the frame initialized to the right size
#Override
public Dimension getPreferredSize() {
return new Dimension(DEFAULT_CANVAS_SIZE, DEFAULT_CANVAS_SIZE);
}
//Paints the graph whenever it needs updating
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
super.paintComponent(g2);
xScale = getWidth() / (xMax - xMin);
yScale = getHeight() / (yMax - yMin);
g2.setColor(Color.BLUE);
try {
g2.draw(function);
} catch (NullPointerException e) {
e.printStackTrace();
}
}
};
/**
* I'm using the function y = exp(x) as an example. Feel free to replace it.
* #param x the x-value
* #return exp(x), the y-value
*/
private static double getYVal(double x) {
return Math.exp(x);
}
//Initializes the function path
public static void initPath() {
final double STEP_X = (xMax - xMin) / canvas.getWidth();
function = new Path2D.Double();
double x = xMin;
double y = getYVal(x);
function.moveTo(getGraphX(x), getGraphY(y));
for(x = xMin + STEP_X; x <= xMax; x += STEP_X) {
y = getYVal(x);
function.lineTo(getGraphX(x), getGraphY(y));
}
}
/**
* Gets the pixel x-coordinate on the canvas
* that corresponds to a given x-value of the function
* #param x the x-value of the function
* #return the adjusted x-coordinate
*/
private static int getGraphX(double x) {
return (int) (canvas.getWidth() * (x - xMin) / (xMax - xMin));
}
//Same thing as getGraphX except for y-coords
private static int getGraphY(double y) {
return (int) (canvas.getHeight() * (yMax - y) / (yMax - yMin));
}
/*
* This is probably where the problem is, this extends the path so that it
* covers the entire visible range
*/
public static void updateFunctionBounds(double dx) {
double newXMin = xMin - dx / xScale;
double newXMax = xMax - dx / xScale;
final int WIDTH = canvas.getWidth();
final double STEP_X = (xMax - xMin) / WIDTH;
double drawTo;
if((drawTo = newXMin) < xMin && drawTo < minXGraphed) {
minXGraphed = drawTo;
double x = drawTo;
function.moveTo(getGraphX(x), getGraphY(getYVal(x)));
for(x = drawTo + STEP_X; x < xMin; x += STEP_X) {
function.lineTo(getGraphX(x), getGraphY(getYVal(x)));
}
x = xMin;
function.lineTo(getGraphX(x), getGraphY(getYVal(x)));
} else if((drawTo = newXMax) > xMax && drawTo > maxXGraphed) {
maxXGraphed = drawTo;
double x = xMax;
function.moveTo(getGraphX(x), getGraphY(getYVal(x)));
for(x = xMax + STEP_X; x < drawTo; x += STEP_X) {
function.lineTo(getGraphX(x), getGraphY(getYVal(x)));
}
x = drawTo;
function.lineTo(getGraphX(x), getGraphY(getYVal(x)));
}
}
public static void main(String[] args) {
initBounds();
initFrame();
}
//Initializes the graph boundaries. Feel free to change this
private static void initBounds() {
xMin = yMin = minXGraphed = -10;
xMax = yMax = maxXGraphed = 10;
}
//Initializes the frame and a MouseAdapter that controls the scrolling
private static void initFrame() {
MouseAdapter adapter = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
lastPointer[0] = e.getX();
lastPointer[1] = e.getY();
}
#Override
public void mouseDragged(MouseEvent e) {
super.mouseDragged(e);
AffineTransform t = new AffineTransform();
double dx = e.getX() - lastPointer[0];
double dy = e.getY() - lastPointer[1];
t.translate(dx, dy);
updateFunctionBounds(dx);
function.transform(t);
xMin -= dx / xScale;
xMax -= dx / xScale;
yMin -= dy / yScale;
yMax -= dy / yScale;
canvas.repaint();
lastPointer[0] = e.getX();
lastPointer[1] = e.getY();
}
};
canvas.addMouseListener(adapter);
canvas.addMouseMotionListener(adapter);
frame = new JFrame("Minimal Reproducible Approach");
frame.setContentPane(canvas);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.pack();
frame.setVisible(true);
initPath();
canvas.repaint();
}
}
If it was working correctly, the stitching would be seamless and the function would smoothly scroll as the mouse pointer was dragged across the canvas, sort of like it does in Desmos. In reality, it doesn't retain the correct height (y-value) and doesn't actually connect to the previous segments in all cases.
Thank you for any and all help!
Edit: I've updated the code into what I think is a minimal reproducible example.
I am making a pong type game in java and I am trying to make the ball bounce off of the walls but whenever the ball hits the ball it just stops, it does not reflect off of the wall and I can't seem to figure out why.
Ball class which handles the ball movement
public class Ball {
private double x;
private double y;
private double time;
private double xreflection=1.0;
private double yreflection=1.0;
private BallTrajectory traj=new BallTrajectory(20, 20);
public Ball(double x, double y) {
this.x=x;
this.y=y;
}
public void tick() {
time+=1.0/60.0;
if(x==0)
xreflection=1.0;
else if(x==Game.Width-15)
xreflection=-1.0;
if(y==0)
yreflection=1.0;
else if(y==Game.Height-15)
yreflection=-1.0;
x+=traj.xvel()*xreflection;
y-=traj.yvel(time)*yreflection;
}
public void render(Graphics g) {
g.setColor(Color.pink);
g.fillOval((int)x, (int)y, 15,15);
}
}
This class handles the trajectory of the ball as it moves in projectile type motion
public class BallTrajectory {
private double initvel;
private double theta;
public BallTrajectory(double initvel, double theta) {
this.initvel=initvel;
this.theta=theta;
}
public double xvel() {
double xvelo=initvel*Math.cos(Math.toRadians(theta));
return xvelo;
}
public double yvel(double time) {
double yvelo=initvel*Math.sin(Math.toRadians(theta))-(9.8*time);
return yvelo;
}
public double xpos(double time) {
double xpos=initvel*Math.cos(Math.toRadians(theta))*time;
return xpos;
}
public double ypos(double time) {
double ypos=initvel*Math.sin(Math.toRadians(theta))*time-.5*9.8*Math.pow(time, 2);
return ypos;
}
Without going through a whole bunch of testing, I would suggest that it is very unlikely that x will ever be exactly equal to Game.Width or 0. Instead, you should be testing that the value is "within bounds" instead, maybe something like...
public void tick() {
time += 1.0 / 60.0;
if (x <= 0) {
xreflection = 1.0;
} else if (x >= Game.Width - 15) {
xreflection = -1.0;
}
if (y <= 0) {
yreflection = 1.0;
} else if (y >= Game.Height - 15) {
yreflection = -1.0;
}
x += traj.xvel() * xreflection;
y -= traj.yvel(time) * yreflection;
}
You should also start taking the time to learn how to debug your code, it is something you will need to do a lot, from desk-checking your logic to using print statements and the debugger
I'm using libgdx in java android studio. i have just started. i'm working on android phone. i not using any cameras. all i want is a sprite bounce of all four sides of the screen without tapping. i tried many codes i thought would work but nope. i hope u guys can help me. I'm expecting an answer soon as possible. Thanks
this is what i have:
SpriteBatch batch;
Texture background;
Sprite backgroundsprite;
Sprite ballsprite;
Texture line;
Texture ballimg;
BitmapFont credits;
BitmapFont input;
BitmapFont play;
float dt;
String string = "";
float ballx;
float bally;
float speedx;
float speedy;
Rectangle screenrect;
Rectangle ballrect;
float screenLeft ;
float screenBottom ;
float screenTop ;
float screenRight ;
#Override
public void create() {
batch = new SpriteBatch();
speedx = 5f * dt;
speedy = 5f * dt;
createsprite();
createbackground();
createtext();
ballx = ballsprite.getX();
bally = ballsprite.getY();
}
#Override
public void render() {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
dt = Gdx.graphics.getDeltaTime();
ballsprite.setPosition(ballx + speedx,ballsprite.getY());
ballsprite.translateX(speedx);
float left = ballrect.getX();
float bottom = ballrect.getY();
float top = bottom + ballrect.getHeight();
float right = left + ballrect.getWidth();
if(left < screenLeft) {
string = "left";
speedx = 5f*dt;
}
if(right > screenRight)
{
string = "right";
speedx = -5f*dt;
}
if(bottom < screenBottom)
{
string = "bottom";
}
if(top > screenTop)
{
string = "top";
}
batch.begin();
backgroundsprite.draw(batch);
ballsprite.draw(batch);
rendertext();
batch.end();
}
public void createbackground() {
background = new Texture("images/BackgroundGodwin.jpg");
backgroundsprite = new Sprite(background);
backgroundsprite.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
screenrect = new Rectangle(0,0,Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
screenLeft = screenrect.getX();
screenBottom = screenrect.getY();
screenTop = screenBottom + screenrect.getHeight();
screenRight = screenLeft + screenrect.getWidth();
}
public void createsprite() {
ballimg = new Texture("images/SpriteGodwin.png");
ballsprite = new Sprite(ballimg);
ballsprite.setScale(0.65f);
ballsprite.setPosition(Gdx.graphics.getWidth()/3,Gdx.graphics.getHeight()/2);
ballrect = new Rectangle(ballsprite.getBoundingRectangle());
}
#Override
public void dispose() {
batch.dispose();
ballimg.dispose();
background.dispose();
credits.dispose();
play.dispose();
input.dispose();
line.dispose();
}
public void createtext(){
play = new BitmapFont(Gdx.files.internal("fonts/realfont.fnt"));
play.setColor(com.badlogic.gdx.graphics.Color.GOLD);
credits = new BitmapFont(Gdx.files.internal("fonts/realfont.fnt"));
credits.setColor(com.badlogic.gdx.graphics.Color.GOLD);
input = new BitmapFont(Gdx.files.internal("fonts/realfont.fnt"));
input.setColor(com.badlogic.gdx.graphics.Color.OLIVE);
}
public void rendertext() {
credits.draw(batch, "Maded", Gdx.graphics.getWidth() / 7 - 50, Gdx.graphics.getHeight() / 9);
play.draw(batch, "Touch the Screen to play!!", Gdx.graphics.getWidth() / 2 - 175, Gdx.graphics.getHeight() - 80);
input.draw(batch, string, Gdx.graphics.getWidth() / 2 - 160, Gdx.graphics.getHeight() - 120);
}
}
I made a very simple version of what you want:
public class BouncyGame extends ApplicationAdapter {
SpriteBatch batch;
Texture ball;
float speedX = 3f;
float speedY = 3f;
int x;
int y;
#Override
public void create () {
batch = new SpriteBatch();
ball = new Texture("ball.png");
x = Gdx.graphics.getWidth()/2;
y = Gdx.graphics.getHeight()/2;
}
#Override
public void render () {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//When the ball's x position is on either side of the screen.
//The width of the sprite is taken into account.
if (x > Gdx.graphics.getWidth() - ball.getWidth()/2 || x < 0 + ball.getWidth()/2) {
//Here we flip the speed, so it bonces the other way.
speedX = -speedX;
}
//Same as above, but with on the y-axis.
if (y > Gdx.graphics.getHeight() - ball.getHeight()/2 || y < 0 + ball.getHeight()/2) {
speedY = -speedY;
}
//Move the ball according to the speed.
x += speedX;
y += speedY;
batch.begin();
//Draw the ball so the center is at x and y. Normally it would be drawn from the lower left corner.
batch.draw(ball, x - ball.getWidth()/2, y - ball.getHeight()/2);
batch.end();
}
}
It will result in the following:
http://gfycat.com/TatteredCarefreeHapuku
There are numerous ways to improve this code, you could for example use vectors, and I wouldn't recommend using it in your final product, but it might help you figure out how to do something like this for your own project.
I am studying java with the book "The Art and Science of Java: An Introduction to Computer Science". One of the practice programs is to create a simple clone of the Breakout game.
I am currently able to load the game, but am having issues with the ball physics. I'm using the simplest physics possible, and can't see why it isn't working.
When the ball hits a wall, it bounces normally, but when it hits the paddle or a brick, it doesn't. I'm using the same code in the collision detection to change the direction that I used with the walls. The collisions are detected, I added a println and watched the console as it went through the collision events, but the direction change is not happening.
package chapter10;
/*
* This program creates a clone of the classic Breakout Game,
* where a player bounces a ball with a paddle to "break" bricks
* along the top of the screen.
*
* Controls: Left: left button, Right: right button
*/
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.RepaintManager;
import javax.swing.Timer;
import acm.program.*;
import acm.graphics.*;
public class BreakoutClone extends GraphicsProgram {
/* components */
private GRect paddle;
private GRect brick;
private GOval ball;
/* static variables */
private static final double PADDLE_HEIGHT = 5;
private static final double BALL_SPEED = 2;
private static final double PADDLE_SPEED = 2;
private static final double ROWS_BRICKS = 6;
private static final double COLUMNS_BRICKS = 10;
private static final double TOP_GAP = 50;
/* variables */
private int numTurns = 3;
private double paddleWidth = 50;
private int dx = 2;
private int dy = 2;
public void init() {
setSize(700, 600);
paddle = new GRect(0, getHeight() - 30, paddleWidth, PADDLE_HEIGHT);
paddle.setFilled(true);
add(paddle);
addBricks();
ball = new GOval(getWidth() / 2, 175, 5, 5);
ball.setFilled(true);
add(ball);
}
public void run() {
animateBall();
// TODO animate paddle
}
public void addBricks() {
double gap = 20;
double brickWidth = getWidth() / COLUMNS_BRICKS;
for (int r = 0; r < ROWS_BRICKS; r++) {
for (int b = 0; b < COLUMNS_BRICKS; b++) {
brick = new GRect(b * brickWidth, gap * r + TOP_GAP,
brickWidth, 10);
brick.setFilled(true);
add(brick);
}
}
}
public void endGame() {
// TODO write end game method
}
public void animateBall() {
while (numTurns > 0) {
ball.move(dx, dy);
pause(15);
/* Look for Object Collision */
GObject topRightObject = getElementAt(ball.getX() + 5, ball.getY());
GObject topLeftObject = getElementAt(ball.getX(), ball.getY());
GObject botRightObject = getElementAt(ball.getX() + 5,
ball.getY() + 5);
GObject botLeftObject = getElementAt(ball.getX(), ball.getY() + 5);
/* Bounce off walls */
if ((ball.getX() >= getWidth() - 5) || ball.getX() <= 0) {
dx = -dx;
}
if (ball.getY() <= 0) {
dy = -dy;
}
if ((ball.getY() >= getHeight() - 5)) {
dy = -dy;
// numTurns--;
// if (numTurns == 0) {
// endGame();
// } else {
// run();
// }
}
/* Bounce off objects, remove bricks */
if (topRightObject != null) {
dy = -dy;
hasCollided(topRightObject);
}
if (topLeftObject != null) {
dy = -dy;
hasCollided(topLeftObject);
}
if (botRightObject != null) {
dy = -dy;
hasCollided(botRightObject);
}
if (botLeftObject != null) {
dy = -dy;
hasCollided(botLeftObject);
}
}
}
private void hasCollided(GObject obj) {
if (obj.equals(paddle)) {
System.out.println("detecting paddle");
} else {
System.out.println("detecting brick");
remove(obj);
}
}
}
Add an if(dy<0) around the part that reverses direction of the ball when it hits the paddle (assuming a negative dy means the ball is heading towards the paddle). This removes the possibility that your direction is being changed, but more than once per collision
I created a method that would move a sprite in any direction to a point at a constant speed. I then added a queuing system for each point to move to.
My problem is that if a point becomes queued, usually only the first movement will be correct, then the sprite will seem to move to random locations for other points. The weird part is if I add a
if((int) x != (int) moveInfo.getTargetX() && (int) y != moveInfo.getTargetY()){
...
}else{
this.setPosition(moveInfo.getTargetX(), moveInfo.getTargetY());
...
}
the sprite corrects itself to its target position after it finishes moving to its random point. I created a debugger and it wasn't the sprites image offsetting either as the x and y values were real.
Here is some code I created that may cause the problem...
x and y are doubles.
Movement to point queuing constructor:
Queue<MoveInfo> moveTo = new LinkedList<MoveInfo>();
MoveTo class
private class MoveInfo{
private double targetX, targetY;
private double deltaX, deltaY;
private double direction;
private double speed;
private boolean ai;
public MoveInfo(double targetX, double targetY, double speed){
this.targetX = targetX;
this.targetY = targetY;
this.speed = speed;
this.deltaX = targetX - x;
this.deltaY = targetY - y;
calculateDirection();
ai = false;
}
public void calculateDirection(){
this.direction = Math.atan2(deltaX, deltaY);
}
... //Setters and getters.
}
Called every game update:
private void moveToUpdate(){
if(!spriteMoving && !moveTo.isEmpty()){
if(!moveTo.peek().isAi()){
moveInfo = moveTo.poll();
spriteMoving = true;
System.out.println("Next X"+moveInfo.targetX+" Y"+moveInfo.targetY);
}
}else if(spriteMoving && moveInfo != null){
if((int) x != (int) moveInfo.getTargetX() && (int) y != moveInfo.getTargetY()){
double nextY = y + (moveInfo.getSpeed() * Math.cos(moveInfo.getDirection()));
double nextX = x + (moveInfo.getSpeed() * Math.sin(moveInfo.getDirection()));
setPosition(nextX, nextY);
}else{
this.setPosition(moveInfo.getTargetX(), moveInfo.getTargetY());
spriteMoving = false;
moveInfo = null;
}
}
}
If you wish to see other parts of the code, let me know in the comments.