My Rotation's are not working - java

I a working on my 'game engine,' and after completing the player class, I realized that the camera movement was not working... I did a bunch of try/catch if/else statements and I narrowed it down to either being a problem with Mouse.getDX not functioning properly, or the glRotate is not working properly...
Here is my game class:
package com.matisse.engine;
import static org.lwjgl.opengl.GL11.GL_POINTS;
import static org.lwjgl.opengl.GL11.glBegin;
import static org.lwjgl.opengl.GL11.glCallList;
import static org.lwjgl.opengl.GL11.glColor3f;
import static org.lwjgl.opengl.GL11.glEnd;
import static org.lwjgl.opengl.GL11.glVertex3f;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import assets.TestBlock;
import com.matisse.world.Chunk;
import com.matisse.world.Level;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
public class Game {
public boolean[] keys;
public XStream xstream;
public Level world;
public File file;
public Camera camera;
public Game() throws LWJGLException {
run();
}
public void run() {
try {
Keyboard.create();
keys = new boolean[256];
xstream = new XStream(new DomDriver());
Level first_world = new Level(0, 0);
Chunk first_chunk = new Chunk();
TestBlock floor = new TestBlock(-10, -2, -10, 10, -1, 10);
first_chunk.voxels.add(floor);
first_world.chunks.add(first_chunk);
first_world.genLists();
String xml = xstream.toXML(first_world);
saveFile("world", xml);
world = (Level) xstream.fromXML(readFile("res/maps/world.xml"));
camera = new Camera(this, world.startx, world.startz);
} catch (LWJGLException e) {
e.printStackTrace();
}
}
public void update() {
if (Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {
Engine.state = State.MENU;
}
mapKeys();
camera.update();
camera.translateCamera();
}
public void draw3D() {
for (Chunk i : world.chunks) {
i.render();
glCallList(i.displayListHandle);
}
}
public void draw2D() {
glBegin(GL_POINTS);
glColor3f(1, 0, 0);
glVertex3f(0, 0, 10);
glEnd();
}
public void mapKeys() {
for (int i = 0; i < keys.length; i++) {
keys[i] = Keyboard.isKeyDown(i);
}
}
public void saveFile(String mapname, String xml) {
FileOutputStream fop = null;
String content = xml;
try {
file = new File("res/maps/" + mapname + ".xml");
fop = new FileOutputStream(file);
// if file doesnt exists, then create it
if (!file.exists()) {
file.createNewFile();
}
// get the content in bytes
byte[] contentInBytes = content.getBytes();
fop.write(contentInBytes);
fop.flush();
fop.close();
System.out.println("Done");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fop != null) {
fop.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public String readFile(String filename) {
StringBuffer result = new StringBuffer();
// The name of the file to open
// This will reference one line at a time
String line = null;
try {
// FileReader reads text files in the default encoding.
FileReader fileReader = new FileReader(filename);
// Always wrap FileReader in BufferedReader.
BufferedReader bufferedReader = new BufferedReader(fileReader);
while ((line = bufferedReader.readLine()) != null) {
result.append(line);
}
// Always close files.
bufferedReader.close();
} catch (FileNotFoundException ex) {
System.out.println("Unable to open file '" + filename + "'");
} catch (IOException ex) {
System.out.println("Error reading file '" + filename + "'");
// Or we could just do this:
// ex.printStackTrace();
}
String product = result.toString();
return product;
}
}
And here is my Camera class: (Note: this is one of the first 'engines' I am trying to do with almost little to no outside reference, so the rest of the code is a bit rustic)
package com.matisse.engine;
import static org.lwjgl.opengl.GL11.glRotatef;
import static org.lwjgl.opengl.GL11.glTranslatef;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.util.vector.Vector3f;
public class Camera {
static float speed = 0.35f;
Vector3f vector = new Vector3f(7, 1, 7);
Vector3f rotation = new Vector3f(0, 1, 0);
Vector3f previous = new Vector3f();
boolean moveForward = false, moveBackward = false, strafeLeft = false,
strafeRight = false;
Game world;
public Camera(Game app, float startx, float starty) {
world = app;
vector.x = startx;
vector.y = starty;
}
public void translateCamera() {
glRotatef(rotation.x, 1, 0, 0);
glRotatef(rotation.y, 0, 1, 0);
glRotatef(rotation.z, 0, 0, 1);
glTranslatef(-vector.x, -vector.y - 1.4f, -vector.z);
}
public void update() {
if (Engine.state == State.GAME) {
Mouse.setGrabbed(true);
} else {
Mouse.setGrabbed(false);
}
updatePreviousVector();
updateMotion();
input();
}
public void input() {
if (world.keys[Keyboard.KEY_W]) {
moveForward = true;
} else {
moveForward = false;
}
if (world.keys[Keyboard.KEY_S]) {
moveBackward = true;
} else {
moveBackward = false;
}
if (world.keys[Keyboard.KEY_A]) {
strafeLeft = true;
} else {
strafeLeft = false;
}
if (world.keys[Keyboard.KEY_D]) {
strafeRight = true;
} else {
strafeRight = false;
}
try {
float mouseDX = Mouse.getDX() * 0.8f * 0.16f;
float mouseDY = Mouse.getDY() * 0.8f * 0.16f;
System.out.println(Mouse.getDX());
if (rotation.y + mouseDX >= 360) {
rotation.y = rotation.y + mouseDX - 360;
} else if (rotation.y + mouseDX < 0) {
rotation.y = 360 - rotation.y + mouseDX;
} else {
rotation.y += mouseDX;
System.out.println(mouseDX);
}
if (rotation.x - mouseDY >= -89 && rotation.x - mouseDY <= 89) {
rotation.x += -mouseDY;
} else if (rotation.x - mouseDY < -89) {
rotation.x = -89;
} else if (rotation.x - mouseDY > 89) {
rotation.x = 89;
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void updatePreviousVector() {
previous.x = vector.x;
previous.y = vector.y;
previous.z = vector.z;
}
public void updateMotion() {
if (moveForward) {
vector.x += Math.sin(rotation.y * Math.PI / 180) * speed;
vector.z += -Math.cos(rotation.y * Math.PI / 180) * speed;
}
if (moveBackward) {
vector.x -= Math.sin(rotation.y * Math.PI / 180) * speed;
vector.z -= -Math.cos(rotation.y * Math.PI / 180) * speed;
}
if (strafeLeft) {
vector.x += Math.sin((rotation.y - 90) * Math.PI / 180) * speed;
vector.z += -Math.cos((rotation.y - 90) * Math.PI / 180) * speed;
}
if (strafeRight) {
vector.x += Math.sin((rotation.y + 90) * Math.PI / 180) * speed;
vector.z += -Math.cos((rotation.y + 90) * Math.PI / 180) * speed;
}
}
}
Please, it may end up being a Wolfenstien clone, but I would like to try to implement mouse usage.

Since I see no location in your code where you load an identity matrix, I can only assume that your camera's transformations are accumulating every time you call translateCamera (...) and that this is the source of your problem.
You have to realize that OpenGL boils down to a very unsophisticated state machine. Without using GLSL and arbitrary per-program matrix uniforms, you have to rely on the matrix stack to manipulate your transformations. Every time you do glRotatef (...) or glTranslatef (...) it multiplies the "current" matrix by a rotation or translation matrix. The current matrix is defined using the Matrix Mode and the top of the matrix stack for that mode.
Unless you load a new matrix into your current matrix, or alter the top of the matrix stack, then every time you call rotate, translate, scale, etc. functions you are actually rotating, translating and scaling relative to the prior state of the matrix.
I have looked at your camera's code and I do not think this is the behavior you want. It looks like you want absolute rotation and translation, and not relative. A call to glLoadIdentity (...) will correct this.

Related

infinite loop stops text field from taking inputs

I am rendering a model on canvas with opengl, therefore using a while loop and I run it under a thread not to disturb the main thread. I also have a text Field where I want to write some text. The text field focuses to my action and takes input but when hovering through the canvas, the text field focuses but I am not able to write something on it. This may be because of the the while loop but I do not know how to overcome this. I want the ui to remain responsive.
Here is my code:
import java.awt.Canvas;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JTextField;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.PixelFormat;
public final class Loader extends JFrame {
public final int FPS = 120; // better cause of animations
public static Loader loader;
public Canvas canvas;
private Viewport viewport;
private JTextField textField;
private Loader() {
setTitle("Test");
setResizable(true);
setSize(320, 285);
setLocationRelativeTo(null);
getContentPane().setLayout(null);
setVisible(true);
canvas = new Canvas();
getContentPane().add(canvas);
canvas.setBounds(10, 24, 280, 163);
textField = new JTextField();
textField.setColumns(10);
textField.setBounds(91, 193, 116, 22);
getContentPane().add(textField);
}
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
// Initialize the frame
loader = new Loader();
new Thread(new Runnable() {
#Override
public void run() {
try {
// render the model
loader.render();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
private static void init(Canvas canvas) throws Exception {
Display.setParent(canvas);
try {
Display.create(new PixelFormat(8, 24, 8, 8));
} catch (Exception ex) {
Display.create(new PixelFormat(8, 24, 8, 0));
}
Display.makeCurrent();
Display.setVSyncEnabled(false);
Display.setSwapInterval(0);
}
private void render() throws Exception {
init(canvas);
loader.viewport = new Viewport(canvas);
long[] timerCache = new long[10];
long timerCur = 0L;
long timerDst = 0L;
long timerLast = 0L;
long timerRate = 1000000L * 1000L / FPS;
int timerCacheIndex = 0;
int timerCacheSize = 0;
boolean minSleep = Runtime.getRuntime().availableProcessors() <= 1;
long[] clockCache = new long[32];
int clockIndex = 0;
int fps = 0;
while (isVisible()) {
long clock = System.nanoTime();
long lastClock = clockCache[clockIndex];
clockCache[clockIndex] = clock;
if (++clockIndex == 32)
clockIndex = 0;
if (lastClock != 0L && clock > lastClock) {
fps = (int) (1000000L * 1000L * 32L / (clock - lastClock));
}
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GL11.glClearColor(.5f, .6f, .9f, 1f);
loader.viewport.render();
if (minSleep)
try {
Thread.sleep(1L);
} catch (Exception ex) {
}
timerCache[timerCacheIndex++] = System.nanoTime();
if (timerCacheSize < timerCacheIndex)
timerCacheSize = timerCacheIndex;
if (timerCacheIndex == 10)
timerCacheIndex = 0;
long time = 0L;
for (int i = 0; i != timerCacheSize; time += timerCache[i++])
;
time /= (long) timerCacheSize;
if (timerCacheSize == 1)
timerLast = time;
timerCur += time - timerLast;
timerLast = time;
timerDst += timerRate;
if (timerDst > timerCur) {
long sleep = timerDst - timerCur;
try {
Thread.sleep(sleep / 1000000L, (int) (sleep % 1000000L));
} catch (Exception ex) {
}
timerCur = timerDst;
for (int i = 0; i != timerCacheSize; ++i)
timerCache[i] += sleep;
timerLast += sleep;
}
}
}
}
This gif may help know how it currently looks.
https://gyazo.com/e6950fd8dd01306c704857e94f214f93.gif
I don't see where is the mistake in here, I already use a thread for the render method.
Edit:
Viewport.java
public Viewport(Canvas canvas) throws LWJGLException {
this.canvas = canvas;
this.scale = 1.0F;
this.pitch = 180.0F;
this.yaw = 0.0F;
this.roll = 0.0F;
}
public void render() {
try {
Dimension size = canvas.getSize();
if (models != null && size.width > 0 && size.height > 0) {
if (width != size.width || height != size.height) {
width = size.width;
height = size.height;
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float c = (float) Math.sqrt((double) (width * width) + (double) (height * height));
glOrtho(0.0F, (float) width, 0.0F, (float) height, -c, c);
glMatrixMode(GL_MODELVIEW);
}
if (Mouse.isButtonDown(0) && !Mouse.isButtonDown(1)) {
yaw -= (float) Mouse.getDX() / 2;
pitch -= (float) Mouse.getDY() / 2;
}
if (Mouse.isButtonDown(0) && Mouse.isButtonDown(1)) {
offset_z += (float) Mouse.getDY();
}
float wheel = (float) Mouse.getDWheel() / 1800.0F;
if (wheel > 1.0F)
wheel = 1.0F;
else if (wheel < -1.0F)
wheel = -1.0F;
scale += scale * wheel;
if (scale < 0.01F)
scale = 0.01F;
for (Model3D model : models) {
float x = (float) width / 2.0F;
float y = ((float) -((100.0F * (scale))) + offset_z) + (float) (height - model.height()) / 2.0F;
float z = 0.0F;
model.render(model, x, y, z, pitch, yaw, roll, scale, scale, scale);
}
Display.update();
}
} catch (Throwable t) {
}
}
Instead of:
new Thread(new Runnable() {
#Override
public void run() {
try {
// render the model
loader.render();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
maybe:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
// render the model
loader.render();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
It will run at AWT dispatching thread, maybe the issue will fade. Swing not being thread safe sometimes makes life hard.
Can you try adding requestFocus() to the end of your render() method of your Loader class and report back what happens?

.getSurface().isValid() without Callback

I am trying to use one MainActivity.java class in Android Studio to control an RC car, I am still in the works as this is an early prototype code.
I am currently struggling on why my surfaceHolder.getSurface().isValid() is returning false every time. I cannot find any help besides implementing a Callback class, however, previously I have been able to use precisely the same lines of code mimicking my surfaceHolder and never needed a Callback class, the only exception was the class extended SurfaceView which this one does not because I can only extend one class. Below is my code:
package com.example.hughman.cccontrol;
import android.graphics.Point;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.SurfaceView;
import android.view.SurfaceHolder;
import android.widget.ImageButton;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
public class MainActivity extends AppCompatActivity implements View.OnClickListener, Runnable{
private ImageButton breakButton, connectButton;
private Bitmap left_thumb, left_thumb_resized, right_thumb_resized;
private Context context;
private int screenW, screenH, thumb_x, thumb_y, drag_x, drag_y, current_thumb = -1;
private Canvas canvas;
private Paint paint = new Paint();
private SurfaceHolder surfaceHolder;
private Thread thread;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
SurfaceView surfaceView = new SurfaceView(this);
paint.setAntiAlias(true);
//puts the app in full screen mode
requestWindowFeature(Window.FEATURE_NO_TITLE);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
surfaceHolder = surfaceView.getHolder();
//initalize screen size
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
screenH = size.x;
screenW = size.x;
//initialize buttons
breakButton = findViewById(R.id.breakbutton);
connectButton = findViewById(R.id.connectbutton);
breakButton.setOnClickListener(this);
connectButton.setOnClickListener(this);
left_thumb = BitmapFactory.decodeResource(this.getResources(), R.drawable.thumbstick);
left_thumb_resized = Bitmap.createScaledBitmap(left_thumb, 500, 500, false);
right_thumb_resized = Bitmap.createScaledBitmap(left_thumb, 500, 500, false);
thread = new Thread(this);
thread.start();
}
#Override
public void run() {
if(current_thumb == -1) {
drawInitialBitmap();
}
else
{
reDraw(drag_x, drag_y);
}
System.out.println("Run Test");
setFPS();
}
public void drawInitialBitmap()
{
thumb_x = 800;
thumb_y = (screenH / 2) + 250;
if (surfaceHolder.getSurface().isValid()) {
System.out.println("Hello");
canvas = surfaceHolder.lockCanvas();
canvas.drawBitmap(left_thumb_resized, thumb_x, thumb_y, paint);
canvas.drawBitmap(right_thumb_resized, thumb_x * 2, thumb_y, paint);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
public void reDraw(int x, int y)
{
if (surfaceHolder.getSurface().isValid()) {
canvas = surfaceHolder.lockCanvas();
if (current_thumb == 1) {
canvas.drawBitmap(left_thumb_resized, x, y, paint);
} else if (current_thumb == 2) {
canvas.drawBitmap(right_thumb_resized, x, y, paint);
}
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
//method for when user selects an xml button
public void onClick(View v) {
if (v == connectButton) {
//prompts to connect to Bluetooth
System.out.println("ConectButton");
}
else if(v == breakButton)
{
System.out.println("BreakButton");
//halt all motors
}
}
public boolean onTouchEvent(MotionEvent event){
int action = event.getAction();
int drag_x = (int) event.getX(); // or getRawX();
int drag_y = (int) event.getY();
switch(action){
case MotionEvent.ACTION_DOWN:
if (drag_x >= thumb_x && drag_x < (thumb_x + left_thumb_resized.getWidth())
&& drag_y >= thumb_y && drag_y < (thumb_y + left_thumb_resized.getHeight())) {
current_thumb = 1;
return true;
}
else if (drag_x >= thumb_x * 2 && drag_x < ((thumb_x * 2) + right_thumb_resized.getWidth())
&& drag_y >= thumb_y && drag_y < (thumb_y + right_thumb_resized.getHeight())) {
current_thumb = 2;
return true;
}
break;
case MotionEvent.ACTION_MOVE:
if(current_thumb != -1)
{
System.out.println(Math.sqrt(Math.pow(drag_x - thumb_x, 2) + Math.pow(drag_y - thumb_y, 2)));
System.out.println(Math.sqrt(Math.pow(drag_x - (thumb_x * 2), 2) + Math.pow(drag_y - thumb_y, 2)));
//move left thumb
return true;
}
break;
case MotionEvent.ACTION_UP:
current_thumb = -1;
drawInitialBitmap();
break;
default:
}
return false;
}
public void setFPS() {
try {
thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Everything runs fine and builds properly, but neither thumb sticks are being drawn. I haven't restricted the movement of the thumb sticks, but later on I will add that. As for now, I want to be able to drag them in the direction I want to move, using the distance function to simply figure out how far I ideally want to be able to drag them.
Is it possible to do this without extending SufaceView? Or making a Callback class? Why? Below I will add the code from my previous project where I did not use a Callback class, keep in mind the code is messy as we had multiple people working on the project and it had a required deadline of 24 hours.
package com.example.michael.doyouknowdeway;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.widget.ImageButton;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.media.MediaPlayer;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Created by michael on 1/27/18.
* The big class that does most of the work for the code, sets the overall view of the game, getting
* the character, tiles, and player motions all together for a working game. Took us the most time
* to work on with many bugs occuring along de wae.
*/
public class GameView extends SurfaceView implements Runnable{
//
volatile boolean isPlaying = true, init = true, isPassOver = true;
private Thread gameThread = null;
private SurfaceHolder surfaceHolder;
private Canvas canvas;
private Context context;
private Activity activity;
private int screenWidth = 0, screenHeight = 0, move_const = 1;
private Player player;
private MediaPlayer jumpNoise, eatNoise;
private Bitmap backgroundImage;
private MediaPlayer backgroundMusic;
private MediaPlayer endGameSound;
private Bitmap backgroundImageResized;
Tile currentTile, nextTile;
private ScheduledExecutorService executorService;
private Paint paint = new Paint();
private Paint textPaint = new Paint();
private FireBall fireball;
private int scoreCount = 0, passOver;
private Bitmap endImage;
private Bitmap endImageResized;
private Bitmap run1, podCount;
private Bitmap run1Resized, podCountResized;
private Bitmap run2;
private Bitmap playerJumpImage;
private boolean isRun1 = false;
private ImageButton redoButton;
//starts of the program, creating the background for the game based on the dimensions of the
//phone being used
public GameView(Context context, int screenX, int screenY) {
super(context);
//load images into game
backgroundImage = BitmapFactory.decodeResource(context.getResources(), R.drawable.background_sky);
backgroundImageResized = Bitmap.createScaledBitmap(backgroundImage, screenX, screenY, false);
podCount = BitmapFactory.decodeResource(context.getResources(), R.drawable.detergent_pod);
run1 = BitmapFactory.decodeResource(context.getResources(), R.drawable.knuckles_run);
run1Resized = Bitmap.createScaledBitmap(run1, 200, 200, false);
podCountResized = Bitmap.createScaledBitmap(podCount, 100, 100, false);
run2 = BitmapFactory.decodeResource(context.getResources(), R.drawable.ugandan_knuckle);
playerJumpImage = BitmapFactory.decodeResource(context.getResources(), R.drawable.knucklesjump);
//load sounds into game
jumpNoise = MediaPlayer.create(context, R.raw.jump_takeoff);
eatNoise = MediaPlayer.create(context, R.raw.eat_1);
backgroundMusic = MediaPlayer.create(context, R.raw.music_baby);
endGameSound = MediaPlayer.create(context, R.raw.end_game);
//initialize other important stuff
textPaint.setColor(Color.WHITE);
textPaint.setTextSize(72);
textPaint.setStyle(Paint.Style.FILL);
paint.setAntiAlias(true);
screenWidth = screenX;
screenHeight = screenY;
activity = (Activity) context;
backgroundMusic.start();
this.context = context;
player = new Player(context, screenX, screenY);
fireball = new FireBall(context, screenX, screenY);
currentTile = new Tile(context, 3, screenWidth + 400, screenHeight);
currentTile.fillTile();
surfaceHolder = getHolder();
//controls "running" animation
executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
if(!player.isJumping) {
if (!isRun1) {
run1Resized = Bitmap.createScaledBitmap(run1, 200, 200, false);
isRun1 = true;
} else {
run1Resized = Bitmap.createScaledBitmap(run2, 200, 200, false);
isRun1 = false;
}
}
}
}, 0, 200, TimeUnit.MILLISECONDS); //can change "speed" of run by altering the second param
}
/**
* Main game loop
*/
public void run() {
while (isPlaying) {
update();
draw();
setFPS();
}
}
/**
* Redraws the screen in the new positions, creating continuous movement for the game
*/
public void draw() {
if (surfaceHolder.getSurface().isValid()) {
canvas = surfaceHolder.lockCanvas();
canvas.drawColor(Color.WHITE);
canvas.drawBitmap(backgroundImageResized, 0, 0, paint);
canvas.drawBitmap(podCountResized, 0, 0, paint);
String scoreCountStr = Integer.toString(scoreCount);
if(0 >= (currentTile.getLength() * 100) - move_const)
{
currentTile = new Tile(nextTile);
isPassOver = true;
nextTile = null;
move_const = 0;
}
if(init) {
init = false;
for (int i = 0; i < currentTile.getLength(); i++) {
for (int j = currentTile.getHeight() - 1; j >= 0; j--) {
if (currentTile.getBlock(i, j) != null) {
canvas.drawBitmap(currentTile.getBlock(i, j).getImage(), (i * 100), (j * 100) + 10, paint);
}
}
}
}
else
{
for (int i = 0; i < currentTile.getLength(); i++) {
for (int j = currentTile.getHeight() - 1; j >= 0; j--) {
if (currentTile.getBlock(i, j) != null) {
canvas.drawBitmap(currentTile.getBlock(i, j).getImage(), (i * 100) - move_const, (j * 100) + 10, paint);
}
if (nextTile != null) {
if (i < nextTile.getLength() && j < nextTile.getHeight()) {
if (nextTile.getBlock(i, j) != null) {
canvas.drawBitmap(nextTile.getBlock(i, j).getImage(), ((i + currentTile.getLength()) * 100) - move_const, (j * 100) + 10, paint);
}
}
}
}
}
move_const += 10;
}
if(fireball.isShooting) {
canvas.drawBitmap(fireball.getImage(), fireball.getXVal(), fireball.getYVal(), paint);
}
canvas.drawBitmap(run1Resized,player.getXVal(), player.getYVal(), paint);
canvas.drawText(scoreCountStr, 120, 80, textPaint);
//releases the canvas to be redrawn again
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
/**
* updates the positions of everything on the screen if needed
*/
public void update() {
player.update();
if(fireball.isShooting) {
fireball.update(player);
}
if((((currentTile.getLength() - (screenWidth/100) )* 100) - move_const <= 200) && nextTile == null){
nextTile = currentTile.getNextTile();
}
if(player.getYVal() >= screenHeight || player.getXVal() <= 0){
gameOver();
}
detectCollisions();
}
//initially sets player to not be colliding - changed almost instantly
static boolean isColliding = false;
//
/**
* Detects collisions so the player can collect tide pods and jump off of surfaces.
*/
public void detectCollisions(){
int highestY = 9;
int currentX = (250 + move_const)/100;
if(currentX >= currentTile.getLength() && isPassOver)
{
passOver = -10;
isPassOver = false;
}
else
{
passOver= -25; //arbitrary
}
for(int i = 0; i < currentTile.getHeight(); i++)
{
if(currentX >= currentTile.getLength())
{
passOver += 10;
if(nextTile.getBlock(passOver/100, i) != null)
{
if(!nextTile.getBlock(passOver/100, i).isPod())
{
highestY = i;
break;
}
}
}
else if(currentTile.getBlock(currentX, i) != null)
{
if(!currentTile.getBlock(currentX, i).isPod())
{
highestY = i;
break;
}
}
else
{
highestY = -1;
}
}
checkGroundCollision(highestY);
checkTidePodCollision(currentTile, nextTile);
checkForwardCollision(nextTile, currentX, highestY, passOver);
}
/**
* Method used to check if the player has hit a wall in front of them.
* #param next - the next tile
* #param x - player x position
* #param y - player y position
* #param passOver - the location being passed over.
*/
public void checkForwardCollision(Tile next, int x, int y, int passOver)
{
boolean collision = false;
Rect rect = new Rect();
if(next != null && passOver >= 0)
{
rect.top = y * 100;
rect.bottom = screenHeight;
rect.left = passOver + (player.getBitmap().getWidth() / 2);
rect.right = passOver + (player.getBitmap().getWidth() / 2) + 100;
collision = Rect.intersects(player.getHitBox(), rect);
}
else
{
rect.top = y * 100;
rect.bottom = screenHeight;
rect.left = (x+1)* 100;
rect.right = (x+2) * 100;
collision = Rect.intersects(player.getHitBox(), rect);
}
//if collision is true, half player movement until its not true
}
/**
* Method that checks if a player has hit a tidepod, if so, adds to score count
* #param current - the current tile
* #param next - the next tile
*/
public void checkTidePodCollision(Tile current, Tile next)
{
if(next != null && !isPassOver)
{
for(double iter: next.getTidePods())
{
int x = (int) iter;
int y = (int) (iter - x)*10;
boolean hit = podCollision(x, y);
if(hit)
{
eatNoise.start();
scoreCount += 10;
System.out.println("Cur Next: " + x + " || " + y);
nextTile.setNullBlock(x, y);
return;
}
}
}
else
{
for(double iter: current.getTidePods())
{
int x = (int) iter;
double temp = x;
int y = (int) ((iter - temp)*10.00);
boolean hit = podCollision(x, y);
if(hit)
{
eatNoise.start();
scoreCount += 10;
System.out.println("Current: " + x + " || " + y);
currentTile.setNullBlock(x, y);
return;
}
}
}
}
//compliment to the previous method
private boolean podCollision(int x, int y) {
Rect tideRect = new Rect();
if(isPassOver && x < 3)
{
System.out.println("TEST: " + x + " || " + y);
tideRect.top = (y * 100);
tideRect.left = x * 100 + passOver + 10;
tideRect.right = (x + 2) * 100 + passOver;
tideRect.bottom = (y + 2) * 100 + 10;
}
else {
tideRect.top = y * 100;
tideRect.left = x * 100 - move_const + 10;
tideRect.right = (x + 2) * 100 - move_const;
tideRect.bottom = (y + 2) * 100 + 10;
}
return Rect.intersects(player.getHitBox(), tideRect);
}
/**
* Method used for jumping off of the ground.
* #param highestY
*/
private void checkGroundCollision(int highestY) {
Rect blockRect = new Rect();
boolean GroundCollision;
if(highestY >= 0) {
blockRect.top = (highestY) * 100 - 25;
blockRect.left = 200;
blockRect.right = 300;
//changed this valued -- this is to remind myself
blockRect.bottom = highestY * 100 + 25; //still needs work //make player hitbox just his feet
GroundCollision = Rect.intersects(player.getFeetBox(), blockRect);
System.out.println("WWWWWW : " + GroundCollision);
}
else
{
GroundCollision = false;
}
if(GroundCollision){
isColliding = true;
} else {
player.isFalling = true;
isColliding = false;
}
}
/**
* Sets the FPS to roughly 60 fps
*/
public void setFPS() {
try {
gameThread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* Method that
*/
public void gameOver() {
//end image is not currently working
endImage = BitmapFactory.decodeResource(context.getResources(), R.drawable.end_game);
endImageResized = Bitmap.createScaledBitmap(endImage, 100, 200, false);
canvas.drawBitmap(endImageResized, screenWidth/2, screenHeight/2, paint);
//free up memory from bitmaps
backgroundImage.recycle();
backgroundImage = null;
backgroundImageResized.recycle();
backgroundImageResized = null;
podCount.recycle();
podCount = null;
podCountResized.recycle();
podCountResized = null;
backgroundImage.recycle();
backgroundImage = null;
run1.recycle();
run1 = null;
run1Resized.recycle();
run1Resized = null;
run2.recycle();
run2 = null;
playerJumpImage.recycle();
playerJumpImage = null;
backgroundMusic.stop();
Runtime.getRuntime().gc(); //manually run garbage collector
endGameSound.start();
context.startActivity(new Intent(context,MainActivity.class));
}
/**
* Method used to dictate what to do when the android screen is touched.
* #param event - the type of touch on the screen
* #return - true when screen is touched
*/
public boolean onTouchEvent(MotionEvent event){
int touchAction = event.getAction();
if(touchAction == MotionEvent.ACTION_DOWN){
if(event.getX() < (screenWidth / 2)) {
jumpNoise.start();
if(!player.isJumping && !player.isFalling) {
player.setYval(player.getYVal());
player.isJumping = true;
run1Resized = Bitmap.createScaledBitmap(playerJumpImage, 200, 200, false);
}
else if(player.getJumpCount() < 1)
{
player.setYval(player.getYVal());
player.incrJump();
player.isJumping = true;
run1Resized = Bitmap.createScaledBitmap(playerJumpImage, 200, 200, false);
}
} else {
fireball.setOnScreen(true);
}
}
return true;
}
/**
* Method that pauses the game when necessary (i.e when home button is pressed)
*/
public void pause() {
isPlaying = false;
backgroundMusic.pause();
try {
gameThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* Resumes the game after a pause
*/
public void resume() {
isPlaying = true;
backgroundMusic.start();
gameThread = new Thread(this);
gameThread.start();
}
}
Thank you for your time & any help is appreciated!
EDIT: Also using bitmaps to redraw etc. as that is what I am used to.

How can I export as Runnable JAR file from eclipse?

When I try to export as a Runnable JAR file, I have no options for 'Launch configuration'. Why is this? I am new to programming, can anyone help me?
It's a code for the game Snake. Do I need a main()?
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.util.LinkedList;
import java.util.Random;
import javax.swing.JOptionPane;
#SuppressWarnings("serial")
public class snakeCanvas extends Canvas implements Runnable, KeyListener
{
private final int BOX_HEIGHT = 15;
private final int BOX_WIDTH = 15;
private final int GRID_HEIGHT = 25;
private final int GRID_WIDTH = 25;
private LinkedList<Point> snake;
private Point fruit;
private int direction = Direction.NO_DIRECTION;
private Thread runThread; //allows us to run in the background
private int score = 0;
private String highScore = "";
private Image menuImage = null;
private boolean isInMenu = true;
public void paint(Graphics g)
{
if (runThread == null)
{
this.setPreferredSize(new Dimension(640, 480));
this.addKeyListener(this);
runThread = new Thread(this);
runThread.start();
}
if (isInMenu)
{
DrawMenu(g);
//draw menu
}
else
{
if (snake == null)
{
snake = new LinkedList<Point>();
GenerateDefaultSnake();
PlaceFruit();
}
if (highScore.equals(""))
{
highScore = this.GetHighScore();
System.out.println(highScore);
}
DrawSnake(g);
DrawFruit(g);
DrawGrid(g);
DrawScore(g);
//draw everything else
}
}
public void DrawMenu(Graphics g)
{
if (this.menuImage == null)
{
try
{
URL imagePath = snakeCanvas.class.getResource("snakeMenu.png");
this.menuImage = Toolkit.getDefaultToolkit().getImage(imagePath);
}
catch (Exception e)
{
e.printStackTrace();
}
}
g.drawImage(menuImage, 0, 0, 640, 480, this);
}
public void update(Graphics g)
{
Graphics offScreenGraphics;
BufferedImage offscreen = null;
Dimension d= this.getSize();
offscreen = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB);
offScreenGraphics = offscreen.getGraphics();
offScreenGraphics.setColor(this.getBackground());
offScreenGraphics.fillRect(0, 0, d.width, d.height);
offScreenGraphics.setColor(this.getForeground());
paint(offScreenGraphics);
g.drawImage(offscreen, 0, 0, this);
}
public void GenerateDefaultSnake()
{
score = 0;
snake.clear();
snake.add(new Point (0,2));
snake.add(new Point(0,1));
snake.add(new Point(0,0));
direction = Direction.NO_DIRECTION;
}
public void Move() //adds a new 'block' at front of snake, deleting the back 'block' to create movement
{
Point head = snake.peekFirst(); //grab the first item with no problems
Point newPoint = head;
switch (direction) {
case Direction.NORTH:
newPoint = new Point(head.x, head.y - 1); //vector of -j
break;
case Direction.SOUTH:
newPoint = new Point(head.x, head.y + 1);
break;
case Direction.WEST:
newPoint = new Point(head.x - 1, head.y);
break;
case Direction.EAST:
newPoint = new Point (head.x + 1, head.y);
break;
}
snake.remove(snake.peekLast()); //delete the last block
if (newPoint.equals(fruit))
{
//the snake hits the fruit
score+=10;
Point addPoint = (Point) newPoint.clone(); //creating the end piece upon eating fruit
switch (direction) {
case Direction.NORTH:
newPoint = new Point(head.x, head.y - 1); //vector of -j
break;
case Direction.SOUTH:
newPoint = new Point(head.x, head.y + 1);
break;
case Direction.WEST:
newPoint = new Point(head.x - 1, head.y);
break;
case Direction.EAST:
newPoint = new Point (head.x + 1, head.y);
break;
}
//the movement upon eating fruit
snake.push(addPoint);
PlaceFruit();
}
else if (newPoint.x < 0 || newPoint.x > (GRID_WIDTH - 1))
{
//snake has gone out of bounds - reset game
CheckScore();
GenerateDefaultSnake();
return;
}
else if (newPoint.y < 0 || newPoint.y > (GRID_HEIGHT - 1))
{
//snake has gone out of bounds - reset game
CheckScore();
GenerateDefaultSnake();
return;
}
else if (snake.contains(newPoint))
{
//running into your tail - reset game
CheckScore();
GenerateDefaultSnake();
return;
}
snake.push(newPoint);
}
public void DrawScore(Graphics g)
{
g.drawString("Score: " + score, 0, BOX_HEIGHT * GRID_HEIGHT + 15);
g.drawString("Highscore:" + highScore, 0, BOX_HEIGHT * GRID_HEIGHT + 30);
}
public void CheckScore()
{
if (highScore.equals(""))
return;
if (score > Integer.parseInt((highScore.split(":")[1])))
{
String name = JOptionPane.showInputDialog("New highscore. Enter your name!");
highScore = name + ":" + score;
File scoreFile = new File("highscore.dat");
if (!scoreFile.exists())
{
try {
scoreFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
FileWriter writeFile = null;
BufferedWriter writer = null;
try
{
writeFile = new FileWriter(scoreFile);
writer = new BufferedWriter(writeFile);
writer.write(this.highScore);
}
catch (Exception e)
{
}
finally
{
try
{
if (writer != null)
writer.close();
}
catch (Exception e) {}
}
}
}
public void DrawGrid(Graphics g)
{
//drawing the outside rectangle
g.drawRect(0, 0, GRID_WIDTH * BOX_WIDTH, GRID_HEIGHT * BOX_HEIGHT);
//drawing the vertical lines
for (int x = BOX_WIDTH; x < GRID_WIDTH * BOX_WIDTH; x+=BOX_WIDTH)
{
g.drawLine(x, 0, x, BOX_HEIGHT * GRID_HEIGHT);
}
//drawing the horizontal lines
for (int y = BOX_HEIGHT; y < GRID_HEIGHT * BOX_HEIGHT; y+=BOX_HEIGHT)
{
g.drawLine(0, y, GRID_WIDTH * BOX_WIDTH, y);
}
}
public void DrawSnake(Graphics g)
{
g.setColor(Color.ORANGE);
for (Point p : snake)
{
g.fillRect(p.x * BOX_WIDTH, p.y * BOX_HEIGHT, BOX_WIDTH, BOX_HEIGHT);
}
g.setColor(Color.BLACK);
}
public void DrawFruit(Graphics g)
{
g.setColor(Color.RED);
g.fillOval(fruit.x * BOX_WIDTH, fruit.y * BOX_HEIGHT, BOX_WIDTH, BOX_HEIGHT);
g.setColor(Color.BLACK);
}
public void PlaceFruit()
{
Random rand = new Random();
int randomX = rand.nextInt(GRID_WIDTH);
int randomY = rand.nextInt(GRID_HEIGHT);
Point randomPoint = new Point(randomX, randomY);
while (snake.contains(randomPoint)) //If the fruit happens to spawn on the snake, it will change position
{
randomX= rand.nextInt(GRID_WIDTH);
randomY= rand.nextInt(GRID_HEIGHT);
randomPoint = new Point(randomX, randomY);
}
fruit = randomPoint;
}
#Override
public void run() {
while (true)
{
repaint();
//runs indefinitely (CONSTANTLY LOOPS)
if (!isInMenu)
Move();
//Draws grid, snake and fruit
//buffer to slow down the snake
try
{
Thread.currentThread();
Thread.sleep(100);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
public String GetHighScore()
{
FileReader readFile = null;
BufferedReader reader = null;
try
{
readFile = new FileReader("highscore.dat");
reader = new BufferedReader(readFile);
return reader.readLine();
}
catch (Exception e)
{
return "Nobody:0";
}
finally
{
try{
if (reader != null)
reader.close();
} catch (IOException e){
e.printStackTrace();
}
}
}
#Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode())
{
case KeyEvent.VK_UP:
if (direction != Direction.SOUTH)
direction = Direction.NORTH;
break;
case KeyEvent.VK_DOWN:
if (direction != Direction.NORTH)
direction = Direction.SOUTH;
break;
case KeyEvent.VK_RIGHT:
if (direction != Direction.WEST)
direction = Direction.EAST;
break;
case KeyEvent.VK_LEFT:
if (direction != Direction.EAST)
direction = Direction.WEST;
break;
case KeyEvent.VK_ENTER:
if (isInMenu)
{
isInMenu = false;
repaint();
}
break;
case KeyEvent.VK_ESCAPE:
isInMenu = true;
break;
}
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
}
Basically, you need a Run configuration that was already used to execute your Java application to have a runnable jar file. This is used to select the correct starting point of the program.
If you have executed your Java application using the Run command (either from the Run menu or the toolbar), a Run configuration was created that executes. However, judging from your question, you have not done so, as you have no entrypoint defined for your application.
For that java uses a static main method with predefined parameters, and any Java class that has such a method can be executed. After you have successfully executed your application, it can be started, and then the created run configuration can be used to export a jar file.

Getting movement speed to continue being useful after stopping

Coming up with a title for this question was really really difficult. So I'm making a game, and I have a method that is used for handling movement called handleVelocity(). The way it works, is that in my game, it updates every tick, looking for character movement. If character movement is below 1 or -1, then it stops the character from moving at all. So I would set the character's movement to 0. But then, the character can no longer move, and I don't know how to get him to continue moving. How do I handle this velocity so that it can continue to move, even after it has previously stopped. I'll post both classes below.
Player class:
import java.io.IOException;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;
public class Player {
public Texture playerTexture;
// Positions & speed
public float xPos = 20.0f; // This is initial
public float yPos = 0.0f; // Same as above.
public float acceleration = 15;
public static int gravityForce = 6;
public static int jumpVelocity = 100;
private float moveSpeed = 4f;
private static int MAX_MOVE_SPEED = 9;
public boolean isSupported = true; // Once again, initial value.
public boolean goingRight, goingLeft, canJump;
// movement methods & constants
public void update() {
handleVelocity();
applyGravity();
checkForSupport();
}
public void handleVelocity() {
float minMoveSpeed = 1;
if (this.moveSpeed < minMoveSpeed && this.moveSpeed > -minMoveSpeed) {
this.moveSpeed = 0;
} else {
float dampening = 0.00002f;
double sign = -(int) Math.signum(moveSpeed);
this.moveSpeed += (float) (dampening * sign);
}
xPos += this.moveSpeed;
}
public Texture grabTexture() {
try {
playerTexture = TextureLoader.getTexture("PNG",
ResourceLoader.getResourceAsStream("res/test_char.png"));
} catch (IOException e) {
e.printStackTrace();
}
return playerTexture;
}
private void checkForSupport() {
if (yPos == 0) {
isSupported = true;
} else if (yPos > 0 /* and is not on a platform */) {
isSupported = false;
}
}
private void applyGravity() {
if (!isSupported) {
yPos -= gravityForce;
}
}
public void printPos(String moveMethod) {
System.out.println(moveMethod + " X: " + xPos + " Y: " + yPos
+ " Going Right: " + goingRight + " Going Left: " + goingLeft
+ " Speed: " + moveSpeed);
}
// movement methods
private void accelerateX(float speed) {
moveSpeed += (float) (speed * 0.0096);
if (moveSpeed >= MAX_MOVE_SPEED) {
moveSpeed = MAX_MOVE_SPEED;
} else if (moveSpeed <= -MAX_MOVE_SPEED) {
moveSpeed = -MAX_MOVE_SPEED;
}
}
#SuppressWarnings("unused")
private void accelerateY() {
}
public void moveRight() {
printPos("Moving Right!");
accelerateX(acceleration);
}
public void moveLeft() {
printPos("Moving Left!");
accelerateX(-acceleration);
}
public void jump() {
printPos("Jumping!");
}
}
Main class:
import com.hasherr.platformer.entity.Player;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import static org.lwjgl.opengl.GL11.*;
public class Main {
private void display() {
try {
Display.setDisplayMode(new DisplayMode(1000, 550));
Display.setTitle("Unnamed Platformer Game");
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(0);
}
// OpenGL
while (!Display.isCloseRequested()) {
Display.update();
Display.sync(60); // sync to 60 fps
initGL();
player.update();
handleKeyboardInput();
}
Display.destroy();
}
private boolean keyboardInUse() {
boolean keyboardInUse;
if (!(Keyboard.isKeyDown(Keyboard.KEY_A)
|| Keyboard.isKeyDown(Keyboard.KEY_D) || Keyboard
.isKeyDown(Keyboard.KEY_SPACE))) {
keyboardInUse = false;
} else {
keyboardInUse = true;
}
return keyboardInUse;
}
private void handleKeyboardInput() {
if (!keyboardInUse()) {
player.goingLeft = false;
player.goingRight = false;
}
if (Keyboard.isKeyDown(Keyboard.KEY_D)) {
player.goingLeft = false;
player.goingRight = true;
player.moveRight();
} else if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
player.goingLeft = true;
player.goingRight = false;
player.moveLeft();
} else if (Keyboard.isKeyDown(Keyboard.KEY_SPACE)) {
player.jump();
}
}
private void initGL() {
// initial OpenGL items for 2D rendering
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glOrtho(0, 1000, 0, 550, 1, -1);
// start rendering player image
player.grabTexture().bind();
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(player.xPos, player.yPos);
glTexCoord2f(1, 0);
glVertex2f(player.xPos + 150, player.yPos);
glTexCoord2f(1, 1);
glVertex2f(player.xPos + 150, player.yPos + 150);
glTexCoord2f(0, 1);
glVertex2f(player.xPos, player.yPos + 150);
glEnd(); // stop rendering this image
}
Player player = new Player();
public static void main(String[] args) {
Main main = new Main();
main.display();
}
}
What triggers the movement? Say player should move when right arrow key is pressed and currently the player is on standing state, in that case set the moveSpeed back to default value 4f.
I know you've already accepted an answer, and the answer provided does do the job, however I feel this needs a better solution and more full explanation.
The code doesn't work as is because in your moveLeft and moveRight methods you are calling accelerateX(acceleration). acceleration value is 15. Lets look at accelerateX method.
private void accelerateX(float speed) {
moveSpeed += (float) (speed * 0.0096);
if (moveSpeed >= MAX_MOVE_SPEED) {
moveSpeed = MAX_MOVE_SPEED;
} else if (moveSpeed <= -MAX_MOVE_SPEED) {
moveSpeed = -MAX_MOVE_SPEED;
}
}
Its multiplying the supplied speed by .0096. 15*.0096 = .144. So here we're adding .144 to moveSpeed. Now let's go to handleVelocity
public void handleVelocity() {
float minMoveSpeed = 1;
if (this.moveSpeed < minMoveSpeed && this.moveSpeed > -minMoveSpeed) {
this.moveSpeed = 0;
} else {
float dampening = 0.00002f;
double sign = -(int) Math.signum(moveSpeed);
this.moveSpeed += (float) (dampening * sign);
}
xPos += this.moveSpeed;
}
This code sets moveSpeed to zero if its less than 1, and greater than -1 to force the player to stop moving.
This can be fixed a number of ways and maintain our nice pseudo-physics. The answer you've accepted breaks the physics system by forcing the movement speed to some value. Maybe this is what you want in a platformer, maybe not. Your choice.
I suggest you decrease minMoveSpeed to something smaller, like .01.

StackOverflowError while saving and loading my object in android

I'm writing an android program and i just ran into a problem.
My program serves to create graphs, run some specific algorithms on them (Dijsktra, BelmanFord, etc.), save the graphs to SD Card and loading them back.
The problem: If i save a little bigger, more complex graph i get a stackoverflow error..
Serialization:
public void createExternalStoragePublicGraph(String filename) {
File dir = Environment.getExternalStoragePublicDirectory("grapher");
try {
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(dir, filename);
FileOutputStream fileOutputStream = new FileOutputStream(file);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(graphDrawerView.getGraph());
objectOutputStream.close();
} catch (IOException e) {
Log.w("ExternalStorage", "Error Writing" + filename, e);
}
}
Deserialization:
public Graph loadExternalStoragePublicGraph(String filename) {
File file = new File(Environment.getExternalStoragePublicDirectory("grapher"), filename);
Graph graph = null;
try {
FileInputStream fint = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fint);
graph = (Graph) ois.readObject();
ois.close();
} catch (Exception e) {
Log.e("Deserialization error:", e.getMessage());
e.printStackTrace();
}
return graph;
}
Graph class:
package com.cslqaai.grapher;
import android.util.Log;
import java.io.Serializable;
import java.util.LinkedList;
public class Graph implements Serializable {
private String name;
private boolean directed = false;
private boolean weighted = false;
private LinkedList<Vertex> vertexes = new LinkedList<Vertex>();
private LinkedList<Edge> edges = new LinkedList<Edge>();
// --------------------------------------------------------------------------------------------------------------------------------------
public Graph(boolean weighted, boolean directed) {
this.directed = directed;
this.weighted = weighted;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void add(AbstractGraphObject ago) {
if (ago instanceof Vertex) {
this.vertexes.add((Vertex) ago);
} else if (ago instanceof Edge) {
this.edges.add((Edge) ago);
} else {
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean isWeighted() {
return this.weighted;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean isDirected() {
return this.directed;
}
// --------------------------------------------------------------------------------------------------------------------------------------\
public LinkedList<Edge> getEdges() {
return this.edges;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public LinkedList<Vertex> getVertexes() {
return this.vertexes;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void reset() {
for (int i = 0; i < this.edges.size(); i++) {
this.edges.get(i).setColorToDefault();
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
void remove(AbstractGraphObject ago) {
if (ago instanceof Vertex) {
Log.d("Graph", "Remove Vertex from graph");
this.vertexes.remove((Vertex) ago);
} else if (ago instanceof Edge) {
Log.d("Graph", "Remove Edge to graph");
this.edges.remove((Edge) ago);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setWeighted(boolean weighted) {
this.weighted = weighted;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setDirected(boolean directed) {
this.directed = directed;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public String getName() {
return this.name;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setName(String name) {
this.name = name;
}
}
// ----------------------------------------------------------------------------------------------------------------------------------------
Vertex class:
package com.cslqaai.grapher;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
public class Vertex extends AbstractGraphObject {
public static final int RADIUS_SIZE = 20;
public static final int SURROUNDING_RADIUS_SIZE = 30;
private static Layer defaultVertexLayer = AbstractGraphObject.defaultLayer;
private static int no = 0;
private static Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Paint paintColored = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Paint textBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final int id;
private String name = null;
private Coord coord;
private Coord cachedCoord;
private ArrayList<Edge> edges = new ArrayList<Edge>();
private boolean colored = false;
// --------------------------------------------------------------------------------------------------------------------------------------
static {
Vertex.paint.setColor(0xFF0FF5F5);
Vertex.paintColored.setColor(Color.RED);
Vertex.textPaint.setStyle(Paint.Style.FILL);
Vertex.textPaint.setColor(Color.BLACK);
Vertex.textPaint.setTextAlign(Paint.Align.CENTER);
Vertex.textPaint.setTextSize(20);
Vertex.textPaint.setTypeface(Typeface.create("Helvetica", Typeface.BOLD));
Vertex.textBgPaint.setStyle(Paint.Style.FILL);
Vertex.textBgPaint.setColor(0xFF0FF5F5);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Vertex(Coord coord) {
super(Vertex.defaultVertexLayer);
this.id = Vertex.no++;
this.coord = coord;
this.recalculate();
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Vertex(int x, int y) {
this(new Coord(x, y));
}
// --------------------------------------------------------------------------------------------------------------------------------------
#Override
public void recalculate() {
this.cachedCoord = new Coord(Math.round((Vertex.baseX + this.coord.getX()) * Vertex.scaleFactor), Math.round((Vertex.baseY + this.coord.getY()) * Vertex.scaleFactor));
this.onScreen = this.cachedCoord.getX() + Vertex.RADIUS_SIZE > 0 && this.cachedCoord.getY() + Vertex.RADIUS_SIZE > 0
&& this.cachedCoord.getX() - Vertex.RADIUS_SIZE < Vertex.screenWidth && this.cachedCoord.getY() - Vertex.RADIUS_SIZE < this.cachedCoord.getY();
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Coord getCoord() {
return this.coord;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Coord getCachedCoord() {
return this.cachedCoord;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public int getRadiusSize() {
return Vertex.RADIUS_SIZE;
}
// --------------------------------------------------------------------------------------------------------------------------------------
#Override
public void draw(Canvas canvas) {
this.recalculate();
if (!this.onScreen) {
return;
}
canvas.drawCircle(this.cachedCoord.getX(), this.cachedCoord.getY(), Vertex.RADIUS_SIZE * Vertex.scaleFactor, Vertex.paint);
if (this.name != null) {
float width = Vertex.textPaint.measureText(this.name) + 10;
float height = Vertex.textPaint.getTextSize() + 5;
canvas.drawRect(this.cachedCoord.getX() - width / 2, this.cachedCoord.getY() - height / 2, this.cachedCoord.getX() + width / 2, this.cachedCoord.getY() + height / 2, Vertex.textBgPaint);
canvas.drawText(this.name, this.cachedCoord.getX(), this.cachedCoord.getY() + height * 0.25f, Vertex.textPaint);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
private boolean searchingCoordOn(float radius, float pX, float pY) {
return this.onScreen && ((Math.pow(pX - this.cachedCoord.getX(), 2) + Math.pow(pY - this.cachedCoord.getY(), 2)) < (Math.pow(radius * Vertex.scaleFactor, 2)));
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean isOnCoord(float pX, float pY) {
return this.searchingCoordOn(Vertex.RADIUS_SIZE, pX, pY);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean isInCoordSurroundings(float pX, float pY) {
return this.searchingCoordOn(Vertex.SURROUNDING_RADIUS_SIZE, pX, pY);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void addEdge(Edge edge) {
if (!this.edges.contains(edge)) {
this.edges.add(edge);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
public ArrayList<Edge> getEdges() {
return this.edges;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void removeEdge(Edge edge) {
if (this.edges.contains(edge)) {
this.edges.remove(edge);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean hasEdge(Edge edge) {
return this.edges.contains(edge);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static void setDefaultLayer(Layer layer) {
Vertex.defaultVertexLayer = layer;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static Layer getDefaultLayer() {
return Vertex.defaultVertexLayer;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static void remove(Vertex vertex) {
Iterator<Edge> edges = vertex.getEdges().iterator();
while (edges.hasNext()) {
Edge e = edges.next();
edges.remove();
Edge.remove(e);
}
Vertex.defaultVertexLayer.remove(vertex);
if (Vertex.graph != null) {
Vertex.graph.remove(vertex);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean hasName() {
return this.name != null;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setName(String name) {
this.name = name;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public String getName() {
return this.name;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public int getId() {
return this.id;
}
// --------------------------------------------------------------------------------------------------------------------------------------
}
Edge class:
package com.cslqaai.grapher;
import android.graphics.*;
import java.util.ArrayList;
import java.util.LinkedList;
public class Edge extends AbstractGraphObject {
public static final float STROKE_WIDTH = 5f;
public static final float SENSOR_WIDTH = 50f;
public static final float SURROUNDING_SENSOR_WIDTH = 15f;
public static final float TRIANGLE_SIZE = 8f;
private static Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Paint paintColored = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Paint textBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Layer defaultEdgeLayer = AbstractGraphObject.defaultLayer;
private static int no = 0;
private final int id;
private int weight = 1;
private Coord cachedSourceCoord;
private Coord cachedTargetCoord;
private Vertex sourceVertex;
private Vertex targetVertex;
private Coord weightCoord;
private boolean colored = false;
// --------------------------------------------------------------------------------------------------------------------------------------
static {
Edge.paint.setColor(0xFFFFFFFF);
Edge.paint.setStrokeWidth(Edge.STROKE_WIDTH * Edge.scaleFactor);
Edge.paint.setStrokeCap(Paint.Cap.ROUND);
Edge.paint.setStyle(Paint.Style.FILL_AND_STROKE);
Edge.paintColored.setColor(0xFFFF0000);
Edge.paintColored.setStrokeWidth(Edge.STROKE_WIDTH * Edge.scaleFactor);
Edge.paintColored.setStrokeCap(Paint.Cap.ROUND);
Edge.paintColored.setStyle(Paint.Style.FILL_AND_STROKE);
Edge.textPaint.setStyle(Paint.Style.FILL);
Edge.textPaint.setColor(Color.BLACK);
Edge.textPaint.setTextAlign(Paint.Align.CENTER);
Edge.textPaint.setTextSize(20);
Edge.textPaint.setTypeface(Typeface.create("Helvetica", Typeface.BOLD));
Edge.textBgPaint.setStyle(Paint.Style.FILL);
Edge.textBgPaint.setColor(Color.WHITE);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Edge(Vertex sourceVertex, Vertex targetVertex) {
super(Edge.defaultEdgeLayer);
this.id = Edge.no++;
this.sourceVertex = sourceVertex;
this.targetVertex = targetVertex;
this.sourceVertex.addEdge(this);
this.targetVertex.addEdge(this);
this.recalculate();
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void recalculate() {
this.cachedSourceCoord = new Coord(Math.round((Edge.baseX + this.sourceVertex.getCoord().getX()) * Edge.scaleFactor), Math.round((Edge.baseY + this.sourceVertex.getCoord().getY()) * Edge.scaleFactor));
this.cachedTargetCoord = new Coord(Math.round((Edge.baseX + this.targetVertex.getCoord().getX()) * Edge.scaleFactor), Math.round((Edge.baseY + this.targetVertex.getCoord().getY()) * Edge.scaleFactor));
Line line = new Line(this.cachedSourceCoord, this.cachedTargetCoord);
this.weightCoord = line.getMiddle();
this.onScreen = Edge.screenBottomLine.hasIntersection(line)
|| Edge.screenLeftLine.hasIntersection(line)
|| Edge.screenRightLine.hasIntersection(line)
|| Edge.screenTopLine.hasIntersection(line)
|| this.cachedSourceCoord.getX() > 0 && this.cachedSourceCoord.getX() < Edge.screenWidth && this.cachedSourceCoord.getY() > 0 && this.cachedSourceCoord.getY() < Edge.screenHeight
|| this.cachedTargetCoord.getX() > 0 && this.cachedTargetCoord.getX() < Edge.screenWidth && this.cachedTargetCoord.getY() > 0 && this.cachedTargetCoord.getY() < Edge.screenHeight;
}
// --------------------------------------------------------------------------------------------------------------------------------------
#Override
public void draw(Canvas canvas) {
this.recalculate();
if (!this.onScreen) {
return;
}
canvas.drawLine(this.cachedSourceCoord.getX(), this.cachedSourceCoord.getY(), this.cachedTargetCoord.getX(), this.cachedTargetCoord.getY(), this.colored ? Edge.paintColored : Edge.paint);
if (Edge.graph != null && Edge.graph.isDirected()) {
Line line = new Line(this.cachedSourceCoord, this.cachedTargetCoord);
Coord v = line.getVector();
float t = (float) ((Vertex.RADIUS_SIZE + 5) / Math.sqrt(Math.pow(v.getX(), 2) + Math.pow(v.getY(), 2)));
Coord t1 = new Coord((int) (this.cachedTargetCoord.getX() - t * v.getX()), (int) (this.cachedTargetCoord.getY() - t * v.getY()));
if (!line.isOnLine(t1)) {
t1 = new Coord((int) (this.cachedTargetCoord.getX() + t * v.getX()), (int) (this.cachedTargetCoord.getY() + t * v.getY()));
}
t = (float) ((Vertex.RADIUS_SIZE + 5 + Edge.TRIANGLE_SIZE) / Math.sqrt(Math.pow(v.getX(), 2) + Math.pow(v.getY(), 2)));
Coord p = new Coord((int) (this.cachedTargetCoord.getX() - t * v.getX()), (int) (this.cachedTargetCoord.getY() - t * v.getY()));
if (!line.isOnLine(p)) {
p = new Coord((int) (this.cachedTargetCoord.getX() + t * v.getX()), (int) (this.cachedTargetCoord.getY() + t * v.getY()));
}
v = line.getNormalVector().getVector();
t = (float) ((Edge.TRIANGLE_SIZE) / Math.sqrt(Math.pow(v.getX(), 2) + Math.pow(v.getY(), 2)));
Coord t2 = new Coord((int) (p.getX() - t * v.getX()), (int) (p.getY() - t * v.getY()));
Coord t3 = new Coord((int) (p.getX() + t * v.getX()), (int) (p.getY() + t * v.getY()));
Path path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
path.moveTo(t1.getX(), t1.getY());
path.lineTo(t2.getX(), t2.getY());
path.lineTo(t3.getX(), t3.getY());
path.lineTo(t1.getX(), t1.getY());
path.close();
canvas.drawPath(path, Edge.paint);
}
if (Edge.graph != null && Edge.graph.isWeighted()) {
float width = Edge.textPaint.measureText(Integer.toString(this.weight)) + 10;
float height = Edge.textPaint.getTextSize() + 5;
canvas.drawRect(this.weightCoord.getX() - width / 2, this.weightCoord.getY() - height / 2, this.weightCoord.getX() + width / 2, this.weightCoord.getY() + height / 2, Edge.textBgPaint);
canvas.drawText(Integer.toString(this.weight), this.weightCoord.getX(), this.weightCoord.getY() + height * 0.25f, Edge.textPaint);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
private boolean searchingCoordOn(float distance, float pX, float pY) {
Coord p = new Coord((int) pX, (int) pY);
Coord v = (new Line(this.cachedSourceCoord, this.cachedTargetCoord)).getNormalVector().getVector();
float t = (float) (distance / Math.sqrt(Math.pow(v.getX(), 2) + Math.pow(v.getY(), 2)));
Coord c1 = new Coord((int) (this.cachedSourceCoord.getX() - t * v.getX()), (int) (this.cachedSourceCoord.getY() - t * v.getY()));
Coord c2 = new Coord((int) (this.cachedSourceCoord.getX() + t * v.getX()), (int) (this.cachedSourceCoord.getY() + t * v.getY()));
Coord c3 = new Coord((int) (this.cachedTargetCoord.getX() - t * v.getX()), (int) (this.cachedTargetCoord.getY() - t * v.getY()));
Coord v1 = new Coord(c2.getX() - c1.getX(), c2.getY() - c1.getY());
Coord v2 = new Coord(c3.getX() - c1.getX(), c3.getY() - c1.getY());
v = Coord.minus(p, c1);
return this.onScreen
&& 0 <= Coord.dotProduct(v, v1) && Coord.dotProduct(v, v1) <= Coord.dotProduct(v1, v1)
&& 0 <= Coord.dotProduct(v, v2) && Coord.dotProduct(v, v2) <= Coord.dotProduct(v2, v2);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean isOnCoord(float pX, float pY) {
return this.searchingCoordOn(Edge.SENSOR_WIDTH / 2, pX, pY);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean isInCoordSurroundings(float pX, float pY) {
return this.searchingCoordOn(Edge.SURROUNDING_SENSOR_WIDTH / 2, pX, pY);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Vertex getSourceVertex() {
return this.sourceVertex;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Vertex getTargetVertex() {
return this.targetVertex;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static void setDefaultLayer(Layer layer) {
Edge.defaultEdgeLayer = layer;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static Layer getDefaultLayer() {
return Edge.defaultEdgeLayer;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static void remove(Edge edge) {
edge.getSourceVertex().removeEdge(edge);
edge.getTargetVertex().removeEdge(edge);
Edge.defaultEdgeLayer.remove(edge);
if (Edge.graph != null) {
Edge.graph.remove(edge);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setWeight(int weight) {
this.weight = weight;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public int getWeight() {
return this.weight;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public int getId() {
return this.id;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setColored() {
this.colored = true;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setColorToDefault() {
this.colored = false;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static ArrayList<Edge> getEdgesBetween(Vertex v1, Vertex v2) {
ArrayList<Edge> edges = v1.getEdges();
ArrayList<Edge> edgesRet = new ArrayList<Edge>();
for (Edge edge : edges) {
if (v2.hasEdge(edge)) {
edgesRet.add(edge);
}
}
return edges;
}
// --------------------------------------------------------------------------------------------------------------------------------------
}
The reason for this is that the "normal" serialization would for each entry recursively invoke another writeObject(e), and for long lists this would give a StackOverflowError. The iterative serialization avoids this.
I found this on stackoverflow. I think my problem is related to my long LinkedLists..
Any ideas, advices how to properly serialize and deserialize my Graph object?
Thank You In Advance!
As a fast fix, you could try substuting the LinkedList List implemetnations with ArrayLists. You didn't give enough details (the exact exception and the implementations of the vertexes and edges would be helpful), but what you quoted might be right: we run out of memory because of the recursive calls. I didn't try, but the ArrayList default Java serialization would avoid this issue.
For a really good solution, the serialization methods of the graph objects could be re-implemented, or a tested and more memory efficient library could be used (eg. kryo).
This question would also be helpful.
Try runnning your program with more RAM. StackOverFlowError means, that the stack is filled and cant take any other method. What you could also do, is run the deserialization in another Thread; They have a seperate stack. There are several ways to do so, I recommend using the classes from java.util.concurrent.

Categories

Resources