Hello fellow programmers,
I am currently writing a small graphical application for my studies that should showcase an AI in a small 2D game. I am kind of stuck though right now.
I have made a class hierarchy with Entity at the top to represent basically any entity, like the player, enemies and obstacles, it has a Node attribute that shall represent the Entity graphically. Entity also has two protected SimpleIntegerProperty objects x and y, to which I bind the layout-properties of the Node attribute, so that whenever I change the values of x and y, the graphical representation moves as well.
So far, I could do anything that doesn't rely on Entity-object coordinates well. Collision works etc.
However, when I tried to implement a system that spawns bullets (little Circle objects) at the place the player character is (the values of both x and y properties), there was a strange offset of double the distance the player has to the upper left corner (to the bottom right). I experimented a little and saw that when I checked the coordinates through the localToScene method, with either the values of both properties or directly the layoutX and layoutY methods of the Node, it would give me coordinates TWICE AS BIG as the actual coordinates.
My pane is 600x600 px big and when I place my player (another object that extends to Entity) at 300,300, everything looks fine, but the localToScene method apparently tells me that it is at double of that, so 600,600. When I go with my player to the bottom right corner and print the coordinates via localToScene, it tells me that it is at 1200,1200.
public void addProjectile(Projectile p)
{
Projectile p2 = new PlayerProjectile(100, 100, 0.5);
projectiles.add(p2);
System.out.println(p2.getSkin().localToScene(p2.getX(), p2.getY()));
System.out.println(p2.getSkin().getLayoutX() + " " + p2.getSkin().getLayoutY());
this.getChildren().add(p2.getSkin());
System.out.println(p2.getSkin().getLayoutX() + " " + p2.getSkin().getLayoutY());
// p.setPosition(p.getSkin().localToScreen(p.getX(), p.getY()));
}
This method creates a Projectile at 100,100 (layoutX and Y confirms that as well). Then, I add that Projectile p2 to an ArrayList (not of importance).
The first print there gives me two coordinates, 200 and 200.
The second print gives me 100 and 100.
The third print gives me 100 and 100 as well (adding to this, which is a Pane, doesn't change anything apparently and is not the cause).
Does anyone have a clue why it just doubles everything? Here is every related class.
PlayerProjectile class:
public class PlayerProjectile extends Projectile
{
public static final int FIRE_RATE = 5; // Higher is slower
public PlayerProjectile(int x, int y, double dir)
{
super(x, y, dir);
range = 150;
speed = 4;
damage = 20;
nx = (int) (speed * Math.cos(angle));
ny = (int) (speed * Math.sin(angle));
}
#Override
public void update()
{
move();
}
#Override
protected void move()
{
x.setValue(x.getValue() + nx);
y.setValue(y.getValue() + ny);
// if (distance() > range)
// {
// remove();
// }
}
}
Projectile class:
public abstract class Projectile extends Entity
{
final protected int xOrigin, yOrigin;
protected double angle;
protected int nx, ny;
protected double distance;
protected double speed, range, damage;
public Projectile(int x, int y, double dir)
{
super(x, y);
skin = new Circle(x, y, 3);
((Circle) skin).setFill(new Color(0, 1, 0, 1));
xOrigin = x;
yOrigin = y;
angle = dir;
// this.getVisual().translateXProperty().bind(this.x);
// this.getVisual().translateYProperty().bind(this.y);
this.getSkin().layoutXProperty().bind(this.x);
this.getSkin().layoutYProperty().bind(this.y);
// this.getVisual().getChildren().add(skin);
}
protected void move()
{
}
public int getX()
{
return x.getValue().intValue();
}
public int getY()
{
return y.getValue().intValue();
}
public int getOriginX()
{
return xOrigin;
}
public int getOriginY()
{
return yOrigin;
}
}
Entity class:
public class Entity
{
private boolean removed = false;
protected Level level;
protected Node skin;
// PROPERTIES FÜR X UND Y KOORDINATEN DES SKINS DES SPIELERS
protected SimpleIntegerProperty x, y;
// VISUELLE REPRÄSENTATION DER ENTITY
// protected Pane visual = new Pane();
public Entity()
{
}
public Entity(int x, int y)
{
this.x = new SimpleIntegerProperty(x);
this.y = new SimpleIntegerProperty(y);
}
public void update()
{
}
public void remove()
{
// Remove from level
removed = true;
}
public void setX(int x)
{
this.x.setValue(x);
}
public void setY(int y)
{
this.y.setValue(y);
}
public void setPosition(Point2D p)
{
this.setX((int) p.getX());
this.setY((int) p.getY());
}
public boolean isRemoved()
{
return removed;
}
public void init(Level level)
{
this.level = level;
}
// public Pane getVisual()
// {
// return visual;
// }
public Node getSkin()
{
return skin;
}
}
localToScene transforms from local coordinates to the scene coordinates. Local coordinates are relative to the origin of the node the method is called for. The transform already includes layoutX and layoutY of this node. Therefore given P = (layoutX, layoutY) the result you get is
Inverse(sceneToLocal) * P = Inverse(Inverse(nodeTransform)) * P
= Inverse(Inverse(Translation(P))) * P
= Translation(P) * P
= 2 * P
Transform the origin in the local coordinate system instead to get the real position:
p.setPosition(p.getSkin().localToScreen(0, 0));
Related
Notes
A year later reviewing this question, I'm of the opinion that this was a poor question to ask on SO, and it's a bit embarrassing. I'm unsure whether to just delete the question or to rewrite it in hopes that it may help someone if they found themselves in my shoes.
Despite reading about setters and getters I was lacking a fundamental understanding of how classes, scope, dependencies, actually worked. Asking this question at the time was of great help to me in putting the two pieces together as it was a push in the right direction.
Edited Question
I've been studying Java by putting together my own projects. I'm currently working on a simple game, however, I've come across something that has me completely stumped. I'm sure the answer is something simple, however, I cannot figure out why this is happening.
I've got a method:
public void inertia () {
y = speedY+y;
x= speedX+x;
}
However it behaves differently in the two classes that I've tried it in.
When calling it in [only] one class the variable y does not seem to change [in the other class]. When calling it in [just the] another (a class responsible for animation the local variable used in that class) and the sprite falls down the screen like one would expect [however, the original class does not update]. I'd just like to know what is causing this behavior.
I included a JLabel to see the variable in the other class, and it does not change.
The desired outcome is for the the corresponding x and y values to change so that the sprite will move about the JPanel
This is the class that I'd like to call it from:
public class Mochi {
private float x;
private float y;
private float speedX = 0;
private float speedY = 3;
private float gravity = 3;
boolean mRunR = true;
public float getSpeedY(){
return this.speedY;
}
public float getSpeedX() {
return this.speedX;
}
public float getX() {
return this.x;
}
public float getY() {
return this.y;
}
// problem method below:
public void inertia () {
y = speedY+y;
x= speedX+x;
}
}
While testing animations, I wrote it in this class and found that it works here:
public class Animation {
Image ms = new ImageIcon("mochirs.png").getImage();
Image mws = new ImageIcon("mochiws.png").getImage();
Image currentSprite;
int aniTime = 1;
int sW = 21;
int sH = 14;
Mochi mochi = new Mochi();
int x = (int) mochi.getX();
int y = (int) mochi.getY();
int speedY = (int) mochi.getSpeedY();
int speedX = (int) mochi.getSpeedX();
boolean mRunR = mochi.mRunR;
public void inertia() {
y = speedY+y;
x = speedX+x;
}
public void draw (Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setClip(x, y, sW, sH);
g2.drawImage(currentSprite, x, y, sW, sH, null);
}
public void setCurrentSprite (){
if (mRunR == true) {
if (aniTime <= 5) {
currentSprite = mws;
aniTime ++;
}else if (aniTime <= 10) {
currentSprite = ms;
aniTime++;
}else {
aniTime = 1;
}
}
}
}
Below is that calls the method:
public class DogLogic extends JPanel {
Animation animation = new Animation();
Mochi mochi = new Mochi();
int refreshRate = 30;
public DogLogic () {
JPanel testPain = new JPanel();
JLabel testLabel2= new JLabel (Integer.toString((int)mochi.getX()));
JLabel tl3 = new JLabel (Integer.toString((int) mochi.getY()));
JLabel tl4 = new JLabel (Integer.toString((int) mochi.getSpeedY()));
testPain.add(testLabel2, BorderLayout.SOUTH);
testPain.add(tl3);
testPain.add(tl4);
add (testPain);
gameStart();
}
public void gameStart() {
Thread gameThread = new Thread() {
public void run() {
while (true) {
gameUpdate();
repaint();
try {
Thread.sleep(1000/refreshRate);
}catch (InterruptedException e) {
System.out.println("An error in gameThread has occured");
}
}
}
};
gameThread.start();
}
public void gameUpdate () {
animation.setCurrentSprite();
mochi.inertia();
animation.intertia();
}
#Override
public void paintComponent (Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
animation.draw(g2);
}
}
Conclusion
I was expecting one class to use variables from another class automagically because I did not understand how classes, scope, and dependencies worked in Java at the time.
If you find yourself with a similar question, research scope, dependency injection, and basic object oriented design patterns.
Animation class has two variables x and y that change when animation.inertia() is called.
However when you call mochi.inertia() in DogLogic Class, the x and y that belong to Mochi Class change. Animation Class x and y do not change at all.
So the x and y remain unchanged in Animation class when only mochi.inertia() is called.
Update gameUpdate in DogLogic Class :
public void gameUpdate () {
animation.setCurrentSprite();
mochi.inertia();
animation.updateXY(this.mochi);
}
And in Animation class :
public void updateXY( Mochi mochi){
x = (int) mochi.getX();
y = (int) mochi.getY();
}
I have made a class for the level generation and have got so far with it:
public class LevelGenerator {
private Sprite environment;
private float leftEdge, rightEdge, minGap, maxGap, y;
public Enemy enemy;
public LevelGenerator(Sprite environment, float leftEdge, float rightEdge,
float minGap, float maxGap) {
this.environment = environment;
this.leftEdge = leftEdge;
this.rightEdge = rightEdge;
this.minGap = minGap;
this.maxGap = maxGap;
}
public void generate(float topEdge){
if(y + MathUtils.random(minGap, maxGap) < topEdge)
return;
y = topEdge;
float x = MathUtils.random(leftEdge, rightEdge);
}
Basically, what I want to happen is for the enemy block to randomly generate on the sides of the screen. Here is the enemy block class (very simple):
public class Enemy extends Sprite{
public Enemy(Sprite sprite) {
super(sprite);
}
#Override
public void draw(Batch spriteBatch){
super.draw(spriteBatch);
}
}
This is what the game looks like at the moment when the block is just simply drawn on the game screen in a static position: http://i.imgur.com/SIt18Qn.png. What I am trying to achieve is for these "enemy" blocks to spawn randomly on either side of the screen but I can't seem to figure out a way to do it with the code I have so far.
Thank you!
I could not test but I think it will be fine, you have a rectangle if you want to see if it collides with another actor, if so updates its position in the update and draw method, and ramdon method start customizing to see if the coordinates, which colicionan be assigned to another actor rectagulo enemy or bye.
public class overFlowEnemy extends Sprite {
private final float maxH = Gdx.graphics.getHeight();
private final float maxW = Gdx.graphics.getWidth();
private Rectangle rectangle;
private Random random = new Random();
private float inttt = 0;
private float randomN = 0;
private boolean hasCollided = false;
public overFlowEnemy(Sprite sprite) {
super(sprite);
crearEnemigo();
rectangle = new Rectangle(getX(), getY(), getWidth(), getHeight());
}
#Override
public void draw(Batch spriteBatch) {
super.draw(spriteBatch);
}
private void crearEnemigo(){
setX(RandomNumber((int)maxW));
setY(RandomNumber((int)maxH));
}
private int RandomNumber(int pos) {
random.setSeed(System.nanoTime() * (long) inttt);
this.randomN = random.nextInt(pos);
inttt += randomN;
return (int)randomN;
}
public Rectangle getColliderActor(){
return this.rectangle;
}
}
the class as this should create a random enemy.
Edit: rereading your question, is that my English is not very good, and I think you wanted to be drawn only on the sides of the screen if so, tell me or adapts the class because when you create thought, which was across the screen.
I just added another class, if you can and want to work as you tell me which is correct, and delete the other.
public class overFlow extends Sprite {
private final float maxH = Gdx.graphics.getHeight();
private final float maxW = Gdx.graphics.getWidth();
private Rectangle rectangle;
private Random random = new Random();
private float inttt = 0;
private float randomN = 0;
private boolean hasCollided = false;
public overFlow(Sprite sprite) {
super(sprite);
crearEnemigo();
rectangle = new Rectangle(getX(), getY(), getWidth(), getHeight());
}
#Override
public void draw(Batch spriteBatch) {
super.draw(spriteBatch);
}
private void crearEnemigo(){
setX(RandomNumber((int)maxW, true));
setY(RandomNumber((int)maxH, false));
}
private int RandomNumber(int pos, boolean w) {
random.setSeed(System.nanoTime() * (long) inttt);
if (w = true){
this.randomN = random.nextInt((pos));
if(randomN % 2 == 0){
randomN = (pos - getWidth());
}else{
randomN = 0; //left screen
}
}else{
this.randomN = random.nextInt(pos - (int)getHeight());
}
inttt += randomN;
return (int)randomN;
}
public Rectangle getColliderActor(){
return this.rectangle;
}
}
I've messed around with Java a little bit and I decided to take a course to learn some more and I have been assigned a fairly easy assignment. I have it mostly done and I know what's left must be simple, but I cannot seem to quite get it. So far I have been able to successfully create a program that has 1 bouncing ball, but I would now like to allow the user to input the amount of balls to bounce. I have tried a few different loops in my Ball class, but none of them work. Would anyone be willing to give a quick hand? I am almost sure it would require either an Array or ArrayList and just storing the balls in it but I have yet to find a solution that works. I have looked at other problems like this on the website but none have quite solved my problem. Thank you if you can help!
Main Class :
public class mainClass {
/**
/**
* Frame to hold a bouncing ball panel, implemented in the BallPanel class.
* Controls the animation of the ball via pauses and calls to BallPanel's move
* method.
*
* #author Michael Peterson modified by Mr O Aug 2012
*/
public static class BallTest extends JFrame {
// size of the window
private static final int WINDOW_WIDTH = 500;
private static final int WINDOW_HEIGHT = 300;
// panel containing the bouncing ball
private BallPanel ballPanel;
/**
* Pause command used to control the speed of the bouncing ball animation.
* Currently pauses for 20 ms. Use smaller values for faster animation and
* vice versa.
*/
public static void pause() {
try {
Thread.sleep(20); // pause for 20 ms
} catch (Exception e) {
System.out.println(e);
e.printStackTrace();
}//end of catch
}//end of pause method
/**
* Creates a new instance of BallTest
*/
public BallTest() {
super("Bouncing Ball"); // set frame name
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
setLayout(new BorderLayout());
ballPanel = new BallPanel();
add(ballPanel);
center(this);
setVisible(true);
// infinite animation loop, program halts when window is closed.
while (true) {
pause();
ballPanel.move(ballPanel);
}//end of while loop of animation
} //end of BallTest Constructor
/**
* Helper routine to center a frame on the screen (will cause problems if
* frame is bigger than the screen!)
*/
public static void center(JFrame frame) {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
Point center = ge.getCenterPoint();
int w = frame.getWidth();
int h = frame.getHeight();
int x = center.x - w / 2, y = center.y - h / 2;
frame.setBounds(x, y, w, h);
frame.validate();
}//end of center method
}//end of BallPanel Class
public static void main(String[] args) {
BallTest t = new BallTest(); //make a BallTest object
}//end of main method
}//end of Fall2012Lab11StarterCode class
Ball Class :
public class Ball extends JPanel {
private int bxCoord; //the ball's x coordinate
private int byCoord; //the ball's y coordinate
private int bHeight; //the ball's height
private int bWidth; //the ball's weight
private int bRise; //the ball's y change
private int bRun; //the ball's x change
private Color bColor; //the ball's color
//Constructor
public Ball() {
bxCoord = setStartBxCoord();
byCoord = setStartByCoord();
bHeight = setStartBHeight();
bWidth = setStartBWidth();
bRise = setStartBRise();
bRun = setStartBRun();
bColor = setStartColor();
}
/**
* The setters, getters, and initial value for the ball's x coordinate
*/
public void setBxCoord(int xCoord) {
bxCoord = xCoord;
}
public int setStartBxCoord() {
int xCoord;
xCoord = (int) (Math.random() * 51);
return xCoord;
}
public int getBxCoord() {
return bxCoord;
}
/**
* The setters, getters, and initial value for the ball's y coordinate
*/
public void setByCoord(int yCoord) {
bxCoord = yCoord;
}
public int setStartByCoord() {
int yCoord;
yCoord = (int) (Math.random() * 51);
return yCoord;
}
public int getByCoord() {
return byCoord;
}
/**
* The setters, getters, and initial value for the ball's x height
*/
public void setBHeight(int height) {
bHeight = height;
}
public int setStartBHeight() {
int height;
height = (int) (10 + Math.random() * 11);
return height;
}
public int getBHeight() {
return bHeight;
}
public void setBWidth(int width) {
bWidth = width;
}
/**
* The setters, getters, and initial value for the ball's x width
*/
public int setStartBWidth() {
int width;
width = (int) (10 + Math.random() * 11);
return width;
}
public int getBWidth() {
return bWidth;
}
/**
* The setters, getters, and initial value for the ball's rise
*/
public void setBRise(int rise) {
bRise = rise;
}
public int setStartBRise() {
int rise;
rise = (int) (Math.random() * 11);
return rise;
}
public int getBRise() {
return bRise;
}
/**
* The setters, getters, and initial value for the ball's run
*/
public void setBRun(int run) {
bRun = run;
}
public int setStartBRun() {
int run;
run = (int) (Math.random() * 11);
return run;
}
public int getBRun() {
return bRun;
}
/**
* The movement of the ball in the x and y direction
*/
public void moveX(){
bxCoord += bRun;
}
public void moveY(){
byCoord += bRise;
}
/**
* The setters, getters, and initial value for the ball's color
*/
public void setColor(Color color) {
bColor = color;
}
public Color setStartColor() {
int red = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
Color ranColor = new Color(red, green, blue);
return ranColor;
}
public Color getbColor() {
return bColor;
}
/**
* Computes the next position for the balls and updates their positions.
*/
public void move(BallPanel ballPanel) {
// If ball is approaching a wall, reverse direction
if ((getBxCoord() < (0 - getBRun())) || (getBxCoord() > (ballPanel.getWidth() - getBWidth()))) {
setBRun(-getBRun());
}
if ((getByCoord() < (0 - getBRise())) || (getByCoord() > (ballPanel.getHeight() - getBHeight()))) {
setBRise(-getBRise());
}
// "Move" ball according to values in rise and run
moveX();
moveY();
} // end method move
}//end of Ball Class
Ball Panel Class :
public class BallPanel extends JPanel {
Ball ball = new Ball(); //creat a ball.
/**
* Creates a new instance of BallPanel
*/
public BallPanel() {
super();
}
/**
* The move method moves the ball and repaints the panel
* PreCondtion: A panel containing a ball has been created
* PostCondition: The position of a ball on the panel has moved. The panell
* is repainted with the ball in the new position.
* #param ballPanel The name of the panel on which the ball is found
*/
public void move(BallPanel ballPanel) {
ball.move(ballPanel);
repaint();
}
/**
* Paints the balls at their current positions within the panel.
* PreCondition: A graphics object has been created and needs to be displayed
* PostCondition: The graphics object g has been displayed
* #param g The graphic object to be displayed
*/
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black); // set color black
g.fillRect(0, 0, getWidth(), getHeight()); // paint background
// Paint the Ball
g.setColor(ball.getbColor());
g.fillOval(ball.getBxCoord(), ball.getByCoord(),
ball.getBWidth(), ball.getBHeight());
}//end of paintComponent method
}//end of BallPanel class
Well I'm answering you purely on previous codes i have done similar to this, because I didn't have the time to test out yours.
Your ballPanel class should look something more like this:
import java.util.ArrayList;
public class BallPanel extends JPanel{
private ArrayList<Ball> BallList = new ArrayList<Ball>();
private int num;
public BallPanel(int numberOfBalls){
super();
num = numberOfBalls;
for(int i = 0; i<num; i++){BallList.add(new Ball());}
}
//the rest of your methods, using for loops for the balls
Also I think you can use this instead of ArrayList (this is easier):
Ball[] BallList = new Ball[numberOfBalls];
then an example of your move method should look like this:
public void move(BallPanel ballPanel){
for(int i = 0; i<num; i++){
BallList[i].move(ballPanel);
}
repaint();
}
I had a similar issues with a Bouncing Ball program. Tried the previously posted code, but the public void move(BallPanel area isn't working. When I access an array in that location the ball stops moving. Here is my current code for move:
public void move(BallPanel ballPanel, ArrayList<Ball> ballList) {
for(int i = 0; i<1; i++){
System.out.println("mmm" + i);
ballList.get(i).move(ballPanel);
}
repaint();
Secondly as shown the paintComponent area above, only ball is used to paint the ball. Is that OK that paintComponent is called multiple times but only has a variable ball in it. Here is what that section looks like:
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black); // set color black
g.fillRect(0, 0, getWidth(), getHeight()); // paint background
// Paint the Ball
g.setColor(ball.getbColor());
g.fillOval(ball.getBxCoord(), ball.getByCoord(),
ball.getBWidth(), ball.getBHeight());
I am trying to make a game engine and I want a camera controlled by the player and also effected by other jBullet entities in Java. I got suggested to use Kinematic Objects so I looked up about them. I couldn't find any documentation which I could understand.
Can someone explain how to set up and use kinematic objects or at least show me where I can start?
The documentation for KinematicCharacterController, found here isn't too entirely helpful, but the source in the CharacterDemo can be. Two main properties are defined in the demo.
public KinematicCharacterController character;
public PairCachingGhostObject ghostObject;
The ghost can be used for dynamic collision detection, as it does not automatically react to those events. The character can be moved by changing the Transform.
//from the source src\com\bulletphysics\demos\character\CharacterDemo.java
Transform startTransform = new Transform();
startTransform.setIdentity();
startTransform.origin.set(0.0f, 4.0f, 0.0f);
Vector3f worldMin = new Vector3f(-1000f,-1000f,-1000f);
Vector3f worldMax = new Vector3f(1000f,1000f,1000f);
AxisSweep3 sweepBP = new AxisSweep3(worldMin, worldMax);
ghostObject = new PairCachingGhostObject();
ghostObject.setWorldTransform(startTransform);
sweepBP.getOverlappingPairCache().setInternalGhostPairCallback(new GhostPairCallback());
float characterHeight = 1.75f * characterScale;
float characterWidth = 1.75f * characterScale;
ConvexShape capsule = new CapsuleShape(characterWidth, characterHeight);
ghostObject.setCollisionShape(capsule);
ghostObject.setCollisionFlags(CollisionFlags.CHARACTER_OBJECT);
float stepHeight = 0.35f * characterScale;
character = new KinematicCharacterController(ghostObject, capsule, stepHeight);
dynamicsWorld.addCollisionObject(ghostObject, CollisionFilterGroups.CHARACTER_FILTER, (short)(CollisionFilterGroups.STATIC_FILTER | CollisionFilterGroups.DEFAULT_FILTER));
dynamicsWorld.addAction(character);
It would also be wise to extend the MotionState class to hold the transform
public class MyMotionState extends MotionState {
private Transform worldTransform;
public MyMotionState()
{
worldTransform = new Transform();
worldTransform.setIdentity();
}
#Override
public Transform getWorldTransform(Transform worldTrans)
{
worldTrans.set(worldTransform);
return worldTrans;
}
#Override
public void setWorldTransform(Transform worldTrans)
{
worldTransform.set(worldTrans);
}
}
and linking the Kinematic to a RigidBody for applying the physics to the character, and getting info on rendering.
rigidBody.setCollisionFlags(rigidBody.getCollisionFlags() | CollisionFlags.KINEMATIC_OBJECT);
rigidBody.setActivationState(CollisionObject.DISABLE_DEACTIVATION);
Don't forget to update the physics engine once every iteration of the game loop.
Transform transform = new Transform();
transform.setIdentity();
transform.origin.set(input.getX(), input.getY(), input.getZ());
myMotionState.setWorldTransform(transform);
rigidBody.setCenterOfMassTransform(myMotionState.getWorldTransform());
If you prefer, you could put these in your MainCharacter class or whatever you call it (I like it for the object oriented feel and ease to understand)
public class MainCharacter implements KeyListener, MouseListener
{
private DynamicsWorld world;
private MyMotionState myMotionState;
private RigidBody rigidBody;
private KinematicCharacterController character;
private ConvexShape shape;
private Texture texture;
private GhostObject ghost;
private Vector3f pos;
public MainCharacter(DynamicsWorld world, Vector3f initialPosition, ConvexShape shape, Texture texture)
{
this.world = world;
RigidBodyConstructionInfo constructInfo = new RigidBodyConstructionInfo(...);
this.myMotionState = myMotionState;
rigidBody = new RigidBody(constructInfo);
ghost = new GhostObject();
character = new KinematicCharacterController(ghost,shape,1);
}
public void render()
{
glBegin(GL_QUADS);
glVertex3f(...
...
glEnd();
}
public void mouseMoved(MouseEvent e)
{
//pseudocode
this.yaw = e.getDX();
this.pitch = e.getDY();
}
public void keyPressed(KeyEvent e)
{
Vector3f dPos = null;
if(e.getKeyChar() == 'W')
{
dPos.x = 10;
}
else if(e.getKeyChar() == 'S')
{
dPos.x = -10;
}
etc...
move(dPos.x,dPos.y,dPos.z);
}
public void move(float dx, float dy, float dz) {
pos.z += dx * (float) Math.cos(Math.toRadians(yaw - 90)) + dz * Math.cos(Math.toRadians(yaw));
pos.x -= dx * (float) Math.sin(Math.toRadians(yaw - 90)) + dz * Math.sin(Math.toRadians(yaw));
pos.y += dy * (float) Math.sin(Math.toRadians(pitch - 90)) + dz * Math.sin(Math.toRadians(pitch));
//pseudocode
rigidBody.update(pos);
world.update(pos);
}
}
I hope I have helped you.
For some reason, when my sprite hits the edge on the left it does not rebound. The program was working perfectly, but then I changed something that really should have no effect on it at all and it stopped working.
//Sprite class
public class Sprite {
private String sprite = "sprite-adult.fw.png";
private int speed;
private int dx;
private int dy;
private int x;
private int y;
private Image image;
public Sprite() {
ImageIcon ii = new ImageIcon(getClass().getResource(sprite));
image = ii.getImage();
speed=6;
dx=speed;
dy=speed;
x = 40;
y = 60;
}
public void move() {
toggleRebound();
x += dx;
y += dy;
}
public void toggleRebound() {
if(x == 1366)
dx = negate(dx);
if(y == 768)
dy = negate(dy);
if(x == 0)
dx = negate(dx);
if(y == 0)
dy = negate(dy);
}
public int negate(int x) {
return x*-1;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Image getImage() {
return image;
}
}
//SType class
public class SType extends JFrame{
public SType() {
add(new Board());
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(1366,768);
setLocationRelativeTo(null);
setTitle("S - Type");
setVisible(true);
setResizable(false);
}
public static void main(String[] args) {
new SType();
}
}
//Board class
public class Board extends JPanel implements ActionListener{
private Sprite sprite;
private Timer timer;
public Board() {
setFocusable(true);
setBackground(new Color(39,124,36));
setDoubleBuffered(true);
sprite = new Sprite();
timer = new Timer(5,this);
timer.start();
}
public void paint(Graphics g) {
super.paint(g);
Random rand = new Random(5398);
for(int x=0;x<1000;x++) {
g.setColor(new Color(22,98,19));
g.drawOval(rand.nextInt(1368), rand.nextInt(768), 1, 20);
}
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(sprite.getImage(),sprite.getX(),sprite.getY(),this);
Toolkit.getDefaultToolkit().sync();
}
public void actionPerformed(ActionEvent arg0) {
sprite.move();
repaint();
}
}
The first answerer is correct, this should not affect anything. Though it isn't really relevant in this example, it's good form to initialize private instance variables in the constructor rather than with their declarations.
private int speed;
private int dx;
private int dy;
public Sprite()
{
speed=6;
dx=speed;
dy=speed;
}
There are two lessons to learn here
First, the real problem you're having is that you're checking for rebound by exact equality with zero (or your hardcoded left/top sizes). This means that if you move more than one pixel at a time (as you are, in the code above), you can miss the border and sail through it.
Generally speaking, it's often a good practice to check limits with lessthan-equals, especially when there's a possiblity of "moving" more than one at a time.
For example, if you wanted to write an array class, you wouldn't do your bounds checking with ((idx == 0) or (idx == length)). You'd check for ((idx <= 0) or (idx >= length)), instead.
Same thing with sprite position.
Second, it's always useful to check your assumptions, especially when your expectations are violated. In this case, you were sure that one very simple change had broken your program.
But that wasn't true. It couldn't have been true. Under no circumstances is this:
private int dx = 6;
private int dy = 6;
different from this:
private int speed = 6
private int dx = speed;
private int dy = speed;
So something else changed between your two measurement points (working, non-working). It's entirely possible that this additional change was masked from you somehow - perhaps by a compile error, or maybe you were just looking at some other aspect of the program, and not letting it bounce. But whatever the reason, something else changed.
In this particular case, you probably also changed either your start point or the value of your speed.
But whatever the cause, when you hit an impossible point, you must go back and recheck your assumptions.
But regardless, that's the problem: your sprite is "moving" too fast for your current rebound to catch it. Change to a less-than-equals test, would be my recommendation.
You are incorrect. Making that change, and only that change, did not cause the change in behavior you report.
Most likely, you actually made more than one change, but only tested the rebound behavior after this point.