I'm trying to make a program with graphs and print the graph on the screen. I am using the GraphPainter (a SurfaceView type, implements Runnable) class to paint the graph to the screen. In the drawing function, which loops indefinitely, i am updating the elements of the graph: the node list and the edge list from the Graph class. The node list is perfectly updated from the Graph (when i delete, add or select a node), but the edge list in the Painter don't update if I delete an edge from the actual Graph.
I debugged it and the graphEdgeList from the Graph class is updated correctly when I remove an edge. It's size gets lower. Can anybody tell me why isn't the list from GraphPainter updated accordingly?
GraphDrawer.java:
package com.rares.graphit;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.Window;
import android.view.WindowManager;
public class GraphDrawer extends Activity implements OnTouchListener{
/**Suface Manipulator*/
GraphPainter gp;
/**Graph object*/
public static Graph graph = null;
/**Touch coordinates*/
public static float touchX = 0;
public static float touchY = 0;
/**Drag state*/
public boolean isDragging = false;
/**Drag coordinates*/
public static float dragX = 0;
public static float dragY = 0;
/**Id of the node that is currently selected
* Is -1 if no node is selected during a touch event*/
public static int currentId = -1;
/**Id of a node that was selected before and an action between 2 nodes or on a node
* that needs to be deleted or moved*/
public static int firstId = -1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/*Set window to fullscreen**/
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
gp = new GraphPainter(this);
gp.setOnTouchListener(this);
graph = new Graph();
setContentView(gp);
}
#Override
public boolean onTouch(View view, MotionEvent event) {
// TODO Auto-generated method stub
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
touchX = event.getX();
touchY = event.getY();
currentId = graph.pointIntersectsNode(touchX, touchY);
break;
case MotionEvent.ACTION_MOVE:
dragX = event.getX();
dragY = event.getY();
/**Function will return -1 if there is no intersection or node ID if drag start intersects the node*/
/**Selected node will be red on the screen*/
if ((dragX - touchX)*(dragX - touchX) + (dragY - touchY)*(dragY - touchY) > Graph.Circle.RADIUS_DEFAULT){
if(currentId != -1){
graph.setNodePosition(currentId, dragX, dragY);
}
isDragging = true;
}
break;
case MotionEvent.ACTION_UP:
if(!isDragging){
if(currentId == -1)
graph.addNode(touchX, touchY);
else{
if(!graph.getGraphNodeList().get(currentId).isSelected){
graph.markNodeAsSelected(currentId);
if(firstId == -1)
firstId = currentId;
else{
if(graph.isEdge(Math.min(firstId, currentId), Math.max(firstId, currentId))){
graph.deleteEdge(Math.min(firstId,currentId),Math.max(firstId, currentId));
}
graph.addEdge(Math.min(firstId,currentId),Math.max(firstId, currentId));
graph.markNodeAsDeselected(currentId);
graph.markNodeAsDeselected(firstId);
firstId = -1;
}
}
else{
Log.d("IDS", "ID1: " + Integer.toString(currentId) + "\nSelected: " + Integer.toString(firstId));
/*graph.markNodeAsDeselected(id1);*/
if(firstId == currentId){
graph.deleteNode(currentId);
}
currentId = -1;
firstId = -1;
}
}
}
/*else
if(id != -1){
graph.markNodeAsDeselected(id);
id = -1;
}*/
//Reset values
isDragging = false;
touchX = touchY = dragX = dragY = 0;
break;
}
return true;
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
gp.resume();
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
gp.pause();
}
}
GraphPainter.java:
package com.rares.graphit;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class GraphPainter extends SurfaceView implements Runnable {
/**ArrayLists for drawing the elements of the graph*/
ArrayList<Graph.Node> listNode;
ArrayList<Graph.Edge> listEdge;
/**Holder of surface*/
SurfaceHolder holder;
/**Drawing thread*/
Thread drawThread;
/**Canvas for drawing*/
Canvas canvas = null;
/**Bool for running state*/
boolean isRunning = false;
public GraphPainter(Context context){
super(context);
holder = getHolder();
drawThread = new Thread(this);
isRunning = true;
drawThread.start();
}
#Override
public void run(){
while (isRunning) {
if (holder.getSurface().isValid()) {
canvas = holder.lockCanvas();
/**Draw background*/
canvas.drawRGB(255, 255, 255);
/**Draw Graph*/
drawGraph();
/**Print to screen*/
holder.unlockCanvasAndPost(canvas);
}
}
}
public void drawGraph(){
/**Draw Egdes*/
listEdge = GraphDrawer.graph.getGraphEdgeList();
Log.d("PAINT_EDGES", "Size of listEdge: " + listEdge.size());
for(int i = 0; i < listEdge.size(); ++i){
float startX = listNode.get(listEdge.get(i).id1).drawingCircle.x;
float startY = listNode.get(listEdge.get(i).id1).drawingCircle.y;
float stopX = listNode.get(listEdge.get(i).id2).drawingCircle.x;
float stopY = listNode.get(listEdge.get(i).id2).drawingCircle.y;
Paint linePaint = new Paint();
linePaint.setColor(Graph.NODE_COLOR_UNSELECTED);
canvas.drawLine(startX, startY, stopX, stopY, linePaint);
}
/**Draw Nodes*/
listNode = GraphDrawer.graph.getGraphNodeList();
for(int i = 0; i < listNode.size(); ++i){
canvas.drawCircle(listNode.get(i).drawingCircle.x, listNode.get(i).drawingCircle.y, listNode.get(i).drawingCircle.R, listNode.get(i).drawingCircle.circlePaint);
}
/*clear the arraylists**/
/*listNode.clear();
listEdge.clear();*/
}
/**Will be called from GraphDrawer when onPause() will occur*/
public void pause(){
isRunning = false;
try {
drawThread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
drawThread = null;
}
public void resume(){
drawThread = new Thread(this);
isRunning = true;
drawThread.start();
}
}
Graph.java:
package com.rares.graphit;
import java.util.ArrayList;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.Log;
/**Graph structure and manipulator. Momentarily, it will be an unoriented graph*/
public class Graph {
/**Circle for drawing purposes*/
public static class Circle{
/**Circle Radius setting*/
public static final float RADIUS_DEFAULT = 30;
/**Circle coordinates*/
float x, y;
float R;
/**Circle style*/
Paint circlePaint = null;
/*Circle default color*/
public int DEFAULT_COLOR = Color.BLUE;
public int drawColor;
/**Creates a circle with the Point as center, r as radius, and the default color
* of a node*/
public Circle(float x, float y, float r){
this.x = x;
this.y = y;
this.R = r;
this.circlePaint = new Paint();
this.circlePaint.setColor(DEFAULT_COLOR);
drawColor = DEFAULT_COLOR;
}
}
/**Graph node structure*/
public static class Node{
/**For drawing purposes*/
Circle drawingCircle;
/**Node ID in the graph*/
int ID;
/**Flag that shows wether the node is selected*/
public boolean isSelected = false;
public Node(float x, float y){
this.ID = numberOfNodes;
drawingCircle = new Circle(x, y, Circle.RADIUS_DEFAULT);
}
public void setColor(int color){
this.drawingCircle.circlePaint.setColor(color);
}
}
/**Graph edge structure*/
public static class Edge{
/**Nodes between whom the edge will be drawn
* id1 - the lower number
* id2 - the higher number
* */
public int id1, id2;
/**Constructor that will set the nodes of the edge*/
public Edge(int a, int b){
if(a > b){
id1 = b; id2 = a;
}
else{
id1 = a; id2 = b;
}
}
}
/**List of vicinity of the graph*/
public static ArrayList<Node> graphNodeList;
/**Current number of nodes*/
public static int numberOfNodes = 0;
/**List of the edges of the graph*/
public static ArrayList<Edge> graphEdgeList;
/**Node default colors*/
public static int NODE_COLOR_UNSELECTED = Color.BLUE;
public static int NODE_COLOR_SELECTED = Color.RED;
/**All elements will be added through addNode() or addEdge() methods*/
public Graph(){
graphNodeList = new ArrayList<Graph.Node>(0);
graphEdgeList = new ArrayList<Graph.Edge>(0);
}
/**List of vicinity getter*/
public ArrayList<Node> getGraphNodeList(){
return graphNodeList;
}
/**List of Edges getter*/
public ArrayList<Edge> getGraphEdgeList(){
return graphEdgeList;
}
/**Adds a node into the graph, with the canvas coordinates: x and y*/
public void addNode(float x, float y){
Node newNode = new Node(x, y);
newNode.ID = graphNodeList.size();
graphNodeList.add(newNode);
newNode = null;
}
/**Deletes a node from the graph */
void deleteNode(int id){
graphNodeList.remove(id);
deleteEdges(id);
for(int i = id; i < graphNodeList.size(); ++i){
int aux = graphNodeList.get(i).ID;
if(aux != i)
resetEdgeNodes(aux, i);
graphNodeList.get(i).ID = i;
}
}
/**Verifies if a point described by its coordinates intersects one if the nodes on
* the screen.
* Returns the ID of the node if true
* -1 otherwise*/
public int pointIntersectsNode(float X, float Y){
for(int i = 0; i < graphNodeList.size(); i++){
float centerX = graphNodeList.get(i).drawingCircle.x;
float centerY = graphNodeList.get(i).drawingCircle.y;
float circleRadius = graphNodeList.get(i).drawingCircle.R;
if((centerX-X) * (centerX-X) + (centerY-Y)*(centerY-Y) < circleRadius*circleRadius)
return i;
}
return -1;
}
/**Marks node as selected and will paint it the COLOR_SELECTED color*/
public void markNodeAsSelected(int id){
graphNodeList.get(id).setColor(NODE_COLOR_SELECTED);
graphNodeList.get(id).isSelected = true;
}
/**Marks the node back as deselected, with its default color*/
public void markNodeAsDeselected(int id) {
graphNodeList.get(id).drawingCircle.circlePaint.setColor(NODE_COLOR_UNSELECTED);
graphNodeList.get(id).isSelected = false;
}
/**Sets the position of the node with the id ID at the (X,Y) point*/
public void setNodePosition(int id, float X, float Y) {
graphNodeList.get(id).drawingCircle.x = X;
graphNodeList.get(id).drawingCircle.y = Y;
}
/**Adds an edge between two nodes*/
public void addEdge(int id1, int id2){
Edge edge = new Edge(id1, id2);
graphEdgeList.add(edge);
}
/**Verifies if an edge between nodes id1 and id2 exists*/
public boolean isEdge(int id1, int id2){
for(int i = 0; i < graphEdgeList.size(); ++i){
if(id1 == graphEdgeList.get(i).id1 && id2 == graphEdgeList.get(i).id2)
return true;
}
return false;
}
/**Deletes an edge from the list, by the nodes IDs
* id1 is the node with the minimum id
* id2 is the node with the maximum id
* */
public void deleteEdge(int id1, int id2){
Log.d("EDGE_DELETE", "Size before a single edge deletion: " + Integer.toString(graphEdgeList.size()));
for(int i = 0; i < graphEdgeList.size(); ++i)
if(id1 == graphEdgeList.get(i).id1 && id2 == graphEdgeList.get(i).id2){
Log.d("EDGE", "EDGE DELETED" + Integer.toString(id1) + " " + Integer.toString(id2));
graphEdgeList.remove(i);
Log.d("EDGE_DELETE", "Size after a single edge deletion: " + Integer.toString(graphEdgeList.size()));
return;
}
}
/**Deletes the edges which has one of the nodes with its ID set to id*/
public void deleteEdges(int id){
for(int i = 0; i < graphEdgeList.size(); ++i){
if(id == graphEdgeList.get(i).id1 || id == graphEdgeList.get(i).id2){
graphEdgeList.remove(i);
i--;
}
}
}
/**Resets the List of Edges when a node is deleted*/
public void resetEdgeNodes(int oldId, int newId){
for(int i = 0; i < graphEdgeList.size(); ++i){
int nodeId1 = graphEdgeList.get(i).id1;
int nodeId2 = graphEdgeList.get(i).id2;
if(nodeId1 == oldId){
graphEdgeList.get(i).id1 = newId;
}
else
if(nodeId2 == oldId){
graphEdgeList.get(i).id2 = newId;
}
/*If the new nodes are nod in order, swap them**/
if(graphEdgeList.get(i).id1 > graphEdgeList.get(i).id2){
int aux = graphEdgeList.get(i).id1;
graphEdgeList.get(i).id1 = graphEdgeList.get(i).id2;
graphEdgeList.get(i).id2 = aux;
}
}
}
}
The main problem you are running into is a common issue in multithreaded programming. In java, two of the concerns you need to address are the safety of accessing the graph object from one thread while modifying it from another.
The other concern is related in that both threads may have a different view of the graph object and measures must be taken to let the runtime/compiler know that after certain code boundaries, the view of the graph object must be synchronized.
It would likely help to research synchronized keyword and memory synchronizations. Of course other solutions/optimizations may exist, the best approach is to understand the multithreading concerns mentioned and first take the most straightforward solution .... Deal with performance / optimization later if possible.
Related
So I have an object in Java with a sprite associated to it. I also have an array of 4 different PImages, and I want to gradually change the sprite of that object into each of the PImages in that array (technically “animating” the sprite"), and I want each sprite to last for 0.5 seconds.
Can anyone help me with this?
Here is my approach. "Sprites" is the array containing the 4 images, brickwall is the object, and the event that triggers the sprite change is when a fireball hit the brickwall. I'll provide the code for the parts involved in the question.
for(int i = 0; i < this.fireballs.size(); i++){
//detect collision between fireball and wall
for(int b = 0; b < this.brickwalls.size(); b++){
if((this.fireballs.get(i).getX() <= this.brickwalls.get(b).getX()+10 & this.fireballs.get(i).getX() >= this.brickwalls.get(b).getX()-10) &&
(this.fireballs.get(i).getY() <= this.brickwalls.get(b).getY()+10 & this.fireballs.get(i).getY() >= this.brickwalls.get(b).getY()-10)){
if(this.brickwalls.get(b).counter == 0){
this.brickwalls.get(b).setSprite(wall.sprites[0]);
}
if(this.brickwalls.get(b).counter == 1){
this.brickwalls.get(b).setSprite(wall.sprites[1]);
}
if(this.brickwalls.get(b).counter == 2){
this.brickwalls.get(b).setSprite(wall.sprites[2]);
}
if(this.brickwalls.get(b).counter == 3){
this.brickwalls.get(b).setSprite(wall.sprites[3]);
}
this.fireballs.remove(i);
this.brickwalls.get(b).Exist = false;
//this.brickwalls.remove(b);
if(i == this.fireballs.size()){
break;
}
if(b == this.brickwalls.size()){
break;
}
}
}
}
Here is the wall class. Brickwall is an extension to wall.
package gremlins;
import processing.core.PImage;
import processing.core.PApplet;
public abstract class wall extends PApplet {
protected int x;
protected int y;
protected PImage sprite;
protected int size;
protected boolean Exist = true;
protected int switchTime = 1000;
protected int startTime = 0;
public int savedTime = millis();
public int counter = 0;
public static PImage[] sprites = new PImage[4];
public wall(int x, int y){
this.x = x;
this.y = y;
//load sprites into array
}
public void setSprite(PImage sprite) {
this.sprite = sprite;
}
public void draw(PApplet app){
app.image(this.sprite, this.x, this.y);
if(this.Exist = false){
if(millis() - startTime > 500){
counter++;
startTime = millis();
}
}
}
public int getX(){
return this.x;
}
public int getY(){
return this.y;
}
public void tick(){
}
}
package gremlins;
import processing.core.PImage;
import processing.core.PApplet;
public class brickwall extends wall {
public PImage sprite;
protected PImage[] destroyed = new PImage[4];
public brickwall(int x, int y){
super(x, y);
}
}
I am making a game with android. The player is a rectangle that has to jump over obstacles. When i jump i decrease the height and the y coordinates. When i run the game the rectangle is jumping but when i try to jump over an obstacle its still hitting it. So i think i forget to change a value but i have no idea what value. Sorry for my bad english and i am a beginner at coding.
This is my Player class:
package com.example.niek.speelveld;
import android.graphics.Canvas;
import android.graphics.Paint;
/**
* Created by Niek on 15-8-2017.
*/
public class Player extends GameObject {
private int score;
private boolean up;
private boolean playing;
private long startTime;
private int h = 0;
private boolean jump;
public Player(int x , int y){
super.x = x;
super.y = y;
width = 100;
height = GamePanel.HEIGHT;
score = 0;
startTime = System.nanoTime();
}
public void setUp(boolean b){up = b;}
public void update(){
long elapsed = (System.nanoTime()-startTime)/1000000;
if(elapsed>100){
score++;
startTime = System.nanoTime();
}
if(up && h == 0){
jump = true;
up = false;
}
if (jump) {
if (h < 120) {
h = h + 7;
height = height - 7;
y = y - 7;
} else {
jump = false;
}
} else {
if (h > 0) {
h = h - 7;
height = height + 7;
y = y + 7;
}
}
}
public void draw(Canvas canvas){
Paint myPaint = new Paint();
myPaint.setStyle(Paint.Style.STROKE);
myPaint.setStrokeWidth(7);
canvas.drawRect(x, y, width + x, height, myPaint );
myPaint.setStrokeWidth(1);
canvas.drawText("HIGHSCORE " + score, GamePanel.WIDTH - 100 , 0+myPaint.getTextSize(), myPaint);
}
public int getScore(){return score;}
public boolean getPlaying(){return playing;}
public void setPlaying(boolean b){playing = b;}
//public void resetScore(){score = 0;}
public boolean getJump() {
return jump;
}
}
And in my Gamepanel class i use these 2 methods. When i call the class obstacle.get(i).update(); its only updating the x value so its moving towards the player.
public void update(){
if(player.getPlaying()) {
bg.update();
player.update();
//Add obstacles with timer
long obstacleElapsed = (System.nanoTime()-obstacleStartTime)/1000000;
if(obstacleElapsed >(2000 - player.getScore()/4)){
obstacles.add(new Obstacle(BitmapFactory.decodeResource(getResources(), R.drawable.rsz_spike1), WIDTH + 10, HEIGHT - 51, 14, 51, player.getScore()));
//reset timer
obstacleStartTime = System.nanoTime();
}
}
//Loop through every obstacle and check collision
for(int i = 0; i<obstacles.size(); i++){
obstacles.get(i).update();
if (collision(obstacles.get(i), player)) {
obstacles.remove(i);
player.setPlaying(false);
Intent intent = new Intent(c, Result.class);
intent.putExtra("SCORE", player.getScore());
c.startActivity(intent);
break;
}
//Remove obstacles that are out of the screen
if (obstacles.get(i).getX() < -100) {
obstacles.remove(i);
break;
}
}
}
public boolean collision(GameObject o, GameObject p){
if(Rect.intersects(o.getRectangle(), p.getRectangle())){
return true;
}
return false;
}
#Override
public void draw(Canvas canvas){
//Get Width and Height from screen
final float scaleFactorX = (float)getWidth()/WIDTH;
final float scaleFactorY = (float)getHeight()/HEIGHT;
if(canvas!=null) {
final int savedState = canvas.save();
canvas.scale(scaleFactorX, scaleFactorY);
bg.draw(canvas);
player.draw(canvas);
//Draw obstacles
for(Obstacle o : obstacles ){
o.draw(canvas);
}
canvas.restoreToCount(savedState);
}
}
And finally the Player and the Obstacle class extends Gameobject where i have the getRectangle method.
public Rect getRectangle(){
return new Rect(x, y, x+width, y+height);
}
How can I draw a line on one canvas, while duplicating the line in the exact same points on another canvas. My friends told me to use the same listener events for both, but I can't figure out how to do that. I will give you the code for my canvas and lines.
package com.mypackage.morphing;
import android.content.Context;
import android.graphics.*;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.view.*;
import java.util.ArrayList;
public class FingerLine extends View {
private ArrayList<Line> lines = new ArrayList<Line>();
private final Paint mPaint;
private float startX;
private float startY;
private float endX;
private float endY;
private Line tempL;
private int idx = 0;
private boolean firstCanContext = false;
public FingerLine(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.RED);
}
#Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for(Line l : lines) {
canvas.drawLine(l.startX, l.startY, l.endX, l.endY, mPaint);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lines.add(new Line(event.getX(), event.getY()));
invalidate();
break;
case MotionEvent.ACTION_MOVE:
lines.get(idx).endX = event.getX();
lines.get(idx).endY = event.getY();
invalidate();
break;
case MotionEvent.ACTION_UP: // when the user lifts up
lines.get(idx).endX = event.getX();
lines.get(idx).endY = event.getY();
idx++;
invalidate();
break;
}
return true;
}
public void clearList(){
lines.clear();
idx = 0;
invalidate();
}
public void removeLineNumber(int indexR){
lines.remove(indexR);
idx--;
invalidate();
}
}
package com.mypackage.morphing;
public class Line {
float startX, startY, endX, endY;
public Line(float startX, float startY, float endX, float endY) {
this.startX = startX;
this.startY = startY;
this.endX = endX;
this.endY = endY;
}
public Line(float startX, float startY) { // for convenience
this(startX, startY, startX, startY);
}
}
If you would draw on one canvas and you would like to see the lines drawn appear on the second canvas, simply make a method in your class to "add a line". Whenever you add a line with that method (and subsequently draw it), notify a listener (in this instance, the second canvas) that a line has been added. That second canvas can then decide to draw that line as well (or to fetch the lines from the first canvas and redraw all of them).
So u have 2 classes, both extends View.
FirstView gets the Events when the user touches the screen.
u want SecondView to draw the same things as FirstView.
Your friend says u to add a Listener for both classes.
But u could also send Lines from FirstView to SecondView.
So u create an instance of SecondView in your firstView :
public class FirstView extends View {
SecondView secondView;
Two choices now,
1) Both classes have the ArrayList, when a line is created or modified, u do it in both classes. Dont forget to refresh SecondView !
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Line line = new Line(event.getX(), event.getY());
lines.add(line);
secondView.lines.add(line);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
lines.get(idx).endX = event.getX();
lines.get(idx).endY = event.getY();
secondView.lines.get(idx).endX = event.getX();
secondView.lines.get(idx).endY = event.getY();
2) Only FirstView has the ArrayList, And u draw all lines in both classe in the onDraw method. This is a bad idea because onDraw is called often, so u have to make the least work inside.
I figured out the answer (however it may look scary)
I have 3 classes working on this (not including main, I'll get to that).
The classes are LineController.java, Line.java, and EditingView.java. LineController contains the arraylist's for the objects, lines are the line objects (I just made my own for ease) and EditingView is the types of views I am drawing on top of.
LineController class
public class LineController {
public ArrayList<Line> firstCanvas;
public ArrayList<Line> secondCanvas;
private int idx;
LineController(){
firstCanvas = new ArrayList<>();
secondCanvas = new ArrayList<>();
idx = 0;
}
public void addLine(Line l){
firstCanvas.add(l);
secondCanvas.add(l);
idx++;
}
public void addLine(float x, float y){
firstCanvas.add(new Line(x, y));
secondCanvas.add(new Line(x, y));
idx++;
}
public void addX(int index, float x){
if(index <= idx){
firstCanvas.get(index).endX = x;
secondCanvas.get(index).endX = x;
}
}
public void addX(float x){
firstCanvas.get(firstCanvas.size() - 1).endX = x;
secondCanvas.get(secondCanvas.size() - 1).endX = x;
}
public void addY(int index, float y){
if(index <= idx){
firstCanvas.get(index).endY = y;
secondCanvas.get(index).endY = y;
}
}
public void addY(float y){
firstCanvas.get(firstCanvas.size() - 1).endY = y;
secondCanvas.get(secondCanvas.size() - 1).endY = y;
}
public void clearLists(){
firstCanvas.clear();
secondCanvas.clear();
idx = 0;
}
public boolean removeLast(){
if(firstCanvas.isEmpty()){
return false;
}
if(firstCanvas.size() == 1){
idx = 0;
firstCanvas.clear();
secondCanvas.clear();
}else{
idx--;
firstCanvas.remove(idx);
secondCanvas.remove(idx);
}
return true;
}
}
EditingView class
public class EditingView extends View {
private LineController lc;
private final Paint mPaint;
private boolean drawingMode = true;
private int closestIndex = -1;
private Paint editDot;
private Paint editLine;
private boolean endOfLine;
private boolean noLine = true;
private Point lastTouch;
private final static int MAX_DISTANCE = 100;
private Line editingLine = null;
private int viewIndex;
public EditingView(Context context){
super(context);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
editDot = new Paint(Paint.ANTI_ALIAS_FLAG);
editLine = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.RED);
editDot.setColor(Color.BLUE);
editLine.setColor(Color.CYAN);
}
public EditingView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.RED);
}
public void init(LineController controller){
lc = controller;
}
#Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(viewIndex == 0){ // first View
for (Line l : lc.firstCanvas) {
canvas.drawLine(l.startX, l.startY, l.endX, l.endY, mPaint);
}
if(!drawingMode){
// if in edit, draw a line around the index we found
if(closestIndex != -1) {
canvas.drawCircle(lc.firstCanvas.get(closestIndex).startX,
lc.firstCanvas.get(closestIndex).startY, 20, editDot);
canvas.drawCircle(lc.firstCanvas.get(closestIndex).endX,
lc.firstCanvas.get(closestIndex).endY, 20, editDot);
}
}
}else if(viewIndex == 1){
for (Line l : lc.secondCanvas) {
canvas.drawLine(l.startX, l.startY, l.endX, l.endY, mPaint);
}
if(!drawingMode){
// if in edit, draw a line around the index we found
if(closestIndex != -1) {
canvas.drawCircle(lc.secondCanvas.get(closestIndex).startX,
lc.secondCanvas.get(closestIndex).startY, 20, editDot);
canvas.drawCircle(lc.secondCanvas.get(closestIndex).endX,
lc.secondCanvas.get(closestIndex).endY, 20, editDot);
}
}
}
}
public void drawLine(MotionEvent event){
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lc.addLine(event.getX(), event.getY());
invalidate();
break;
case MotionEvent.ACTION_MOVE:
lc.addX(event.getX());
lc.addY(event.getY());
invalidate();
break;
case MotionEvent.ACTION_UP:
lc.addX(event.getX());
lc.addY(event.getY());
invalidate();
break;
}
}
public void editLine(MotionEvent event){
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
// run function to find the closest point based on that object
lastTouch = new Point((int)event.getX(), (int)event.getY());
editingLine = findClosestLine(); // this should be for any point on the screen
if(editingLine == null){
noLine = true;
}else{
noLine = false;
endOfLine = checkPointStartEnd(editingLine);
}
invalidate();
break;
case MotionEvent.ACTION_MOVE:
// edit the point to have the new points from the touch event
if(!noLine){
// we found one
if(!endOfLine){
editingLine.startX = event.getX();
editingLine.startY = event.getY();
}else{
editingLine.endX = event.getX();
editingLine.endY = event.getY();
}
invalidate();
}
break;
case MotionEvent.ACTION_UP:
if(!noLine){
// we found one
if(!endOfLine){
editingLine.startX = event.getX();
editingLine.startY = event.getY();
}else{
editingLine.endX = event.getX();
editingLine.endY = event.getY();
}
editingLine = null;
invalidate();
}
lastTouch = null;
break;
}
}
public void editMode(int index){
drawingMode = false;
closestIndex = index;
}
public void clear() { closestIndex = -1; }
public void drawingMode(){
drawingMode = true;
}
public void viewIndex(int index){
viewIndex = index;
}
// finds the closest line in either array (checks the screen right?)
private Line findClosestLine(){
int temp1, temp2;
Line tempLine = null;
int closestDistance = MAX_DISTANCE;
// needs to loop through the array list, for both startX,Y and endX,Y of each line in the array
// then needs to get the index to that point and draw a circle around that point
// also change the colour of the line and the corresponding line based on that index
if(viewIndex == 0){
for(int i = 0; i < lc.firstCanvas.size(); i++){
temp1 = checkPoint(lc.firstCanvas.get(i));
if(temp1 < closestDistance && temp1 != -1) {
tempLine = lc.firstCanvas.get(i);
closestIndex = i;
}
}
}else{
for(int i = 0; i < lc.firstCanvas.size(); i++){
temp2 = checkPoint(lc.secondCanvas.get(i));
if(temp2 < closestDistance && temp2 != -1){
tempLine = lc.secondCanvas.get(i);
closestIndex = i;
}
}
}
return tempLine;
}
// Checks the point of a line to see if it is close
private int checkPoint(Line l){
int firstDistance = pyth((lastTouch.x - l.startX), (lastTouch.y - l.startY));
int secondDistance = pyth((lastTouch.x - l.endX), (lastTouch.y - l.endY));
if(MAX_DISTANCE > firstDistance) {
return (int)firstDistance;
}else if(MAX_DISTANCE > secondDistance){
return (int)secondDistance;
}
return -1;
}
// Checks the line we have found for the close point being start or end
private boolean checkPointStartEnd(Line l){
boolean start = false;
int firstDistance = pyth((lastTouch.x - l.startX), (lastTouch.y - l.startY));
int secondDistance = pyth((lastTouch.x - l.endX), (lastTouch.y - l.endY));
if(MAX_DISTANCE < firstDistance) {
start = true;
}else if(MAX_DISTANCE < secondDistance){
start = false;
}
return start;
}
// returns pythagorian theorum
private int pyth(double x, double y){
int z;
z = (int)Math.sqrt(((x * x) + (y * y)));
return z;
}
}
Line Class
public class Line {
float startX, startY, endX, endY;
public Line(float startX, float startY, float endX, float endY) {
this.startX = startX;
this.startY = startY;
this.endX = endX;
this.endY = endY;
}
public Line(float startX, float startY) { // for convenience
this(startX, startY, startX, startY);
}
}
MainActivity (only what's important)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lc = new LineController();
firstCanvas = new EditingView(this);
secondCanvas = new EditingView(this);
firstCanvas.viewIndex(0);
secondCanvas.viewIndex(1);
firstCanvas.init(lc);
secondCanvas.init(lc);
firstCanvas.setOnTouchListener(new TouchListener());
secondCanvas.setOnTouchListener(new TouchListener());
firstFrame = (FrameLayout)findViewById(R.id.firstFrame);
secondFrame = (FrameLayout)findViewById(R.id.secondFrame);
firstFrame.addView(firstCanvas);
secondFrame.addView(secondCanvas);
}
private class TouchListener implements View.OnTouchListener{
#Override
public boolean onTouch(View v, MotionEvent event) {
EditingView temp = (EditingView)v;
if(drawingMode) {
// drawing mode
temp.drawLine(event);
updateCanvas();
}else{
// edit mode
temp.editLine(event);
updateCanvas();
}
return true;
}
}
This is all that I used. What happens is that I have a controller object created inside of the main and passed to the EditViews to hold the arrays. I then access only the arrays depending on the view that is being used. I then setup a touch listener created inside of the MainActivity (could be broken up) class that will be used to handle what mode the user is in, (either draw or edit). Using all this will allow me to create new views (programmatically) using the listener for the whole screen inside of the MainActivity.
I know this isn't the best way to do it all, but it works around and I could not find anything else. Hope this helps others who are looking to do something of the same thing. Or at least work towards their own solution.
2 part question here. I'm having some trouble here with my for loops. The basic idea of my code is that whenever checkX and checkY are called they compare the x of the touch and the x of the player then if the players x is less than the touchx then it increments the player x by 1 until the player x equals touch x. Its the exact same thing for checky. Everything works fine, the problem is that it never stops. The player just keeps moving on the exact same path.
Heres the player class
package com.gametest;
import java.util.concurrent.CopyOnWriteArrayList;
import com.gametest.GameSurfaceView.MyView;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
public class Player {
int ID;
static int x, y, width, height, widthEnd, currentFrame, boundsX, boundsY,
dirTimer, dir, simx, simy;
static Bitmap currentSprite, playerSprite, deathSprite;
static MyView mv;
public Player(MyView v, Bitmap orb, Bitmap explosion, int screenWidth,
int screenHeight) {
// TODO Auto-generated constructor stub
playerSprite = orb;
deathSprite = explosion;
currentSprite = playerSprite;
mv = v;
height = playerSprite.getHeight();
width = playerSprite.getWidth() / 3;
currentFrame = 1;
}
public static void sprite_draw(Canvas canvas,
CopyOnWriteArrayList<Sprite> copy) {
// TODO Auto-generated method stub
x = (mv.getWidth() / 2) - 50;
y = (mv.getHeight() / 2) - 50;
currentFrame = currentFrame + 1;
if (currentFrame > 3) {
currentFrame = 1;
}
widthEnd = (currentFrame * width) + width;
boundsX = x + width;
boundsY = y + height;
Rect src = new Rect(currentFrame * width, 0, widthEnd, height);
Rect dst = new Rect(x, y, boundsX, boundsY);
canvas.drawBitmap(currentSprite, src, dst, null);
for (Sprite s : copy) {
if (s.x + 50 > x && s.x + 50 < boundsX && s.y + 50 > y
&& s.y + 50 < boundsY) {
GameSurfaceView.damagePlayer();
};
}
}
public void checkX(Integer touchx) {
if (touchx > x) {
for (int newx = x; newx < touchx; newx++) {
GameSurfaceView.setDirection(1, 0);
try {
mv.t.sleep (10);
} catch (InterruptedException e) {
}
}
}
if (touchx < x ) {
for (int newx = x; newx > touchx; newx--) {
GameSurfaceView.setDirection(-1, 0);
try {
mv.t.sleep (10);
} catch (InterruptedException e) {
}
}
}
}
public void checkY(int touchy) {
if (touchy > y) {
for (int newy = y; newy < touchy; newy++) {
GameSurfaceView.setDirection(0, 1);
try {
mv.t.sleep (10);
} catch (InterruptedException e) {
}
}
}
if (touchy < y) {
for (int newy = y; newy > touchy; newy--) {
GameSurfaceView.setDirection(0, -1);
try {
mv.t.sleep (10);
} catch (InterruptedException e) {
}
}
}
}
}
And in case its needed here is the surface view class
package com.gametest;
import java.util.concurrent.CopyOnWriteArrayList;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnTouchListener;
public class GameSurfaceView extends Activity implements OnTouchListener {
double ran;
int touchX, touchY, screenWidth, screenHeight, objX, objY;
static boolean canUpdate;
static int enemyCount, score, playerHealth, test1, test2;
static MyView v;
static Bitmap orb, orb2, explosion;
static CopyOnWriteArrayList<Sprite> copy = new CopyOnWriteArrayList<Sprite>();
static String hpString;
static Player player;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
v = new MyView(this);
v.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent me) {
touchX = (int) me.getX();
touchY = (int) me.getY();
for (Sprite sprite : copy) {
sprite.checkTouch(touchX, touchY);
player.checkY(touchY);
player.checkX(touchX);
}
return true;
}
});
canUpdate = true;
screenWidth = v.getWidth();
screenHeight = v.getHeight();
playerHealth = 250;
hpString = "Health " + playerHealth;
ran = 0;
score = 0;
test1 = 5000;
test2 = 5000;
orb = BitmapFactory.decodeResource(getResources(), R.drawable.blue_orb);
orb2 = BitmapFactory.decodeResource(getResources(), R.drawable.red_orb);
explosion = BitmapFactory.decodeResource(getResources(), R.drawable.explosion);
player = new Player(v, orb2, explosion, screenWidth, screenHeight);
createEnemies();
setContentView(v);
}
private void createEnemies() {
if (enemyCount < 5) {
screenWidth = v.getWidth();
screenHeight = v.getHeight();
int listLength = copy.size();
copy.add(new Sprite(v, orb, explosion, screenWidth, screenHeight, listLength));
enemyCount = enemyCount + 1;
}
}
public static void checkECount(int id) {
canUpdate = false;
copy.remove(id);
enemyCount = enemyCount - 1;
int index = 0;
for(Sprite s : copy) {
s.ID = index;
index++;
}
score = score + 10;
canUpdate = true;
}
#Override
protected void onPause() {
super.onPause();
v.pause();
}
#Override
protected void onResume() {
super.onResume();
v.resume();
}
public class MyView extends SurfaceView implements Runnable {
Thread t = null;
SurfaceHolder holder;
boolean isItOk = false;
public MyView(Context context) {
super(context);
holder = getHolder();
}
#Override
public void run() {
while (isItOk == true) {
if (!holder.getSurface().isValid()) {
continue;
}
Canvas c = holder.lockCanvas();
if(canUpdate){
canvas_draw(c);
}
holder.unlockCanvasAndPost(c);
try {
t.sleep (50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
protected void canvas_draw(Canvas canvas) {
canvas.drawARGB(255, 50, 10, 10);
String ranString = "Score " + score;
ran = Math.random() * 5;
String t = "max1" + test1;
String t2 = "max2" + test2;
if (ran > 3) {
createEnemies();
}
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setTextSize(15);
canvas.drawText(hpString, 10, 25, paint);
canvas.drawText(ranString, 10, screenHeight - 25, paint);
for (Sprite sprite : copy) {
sprite.sprite_draw(canvas);
}
Player.sprite_draw(canvas, copy);
}
public void pause() {
isItOk = false;
while (true) {
try {
t.join();
} catch (InterruptedException e) {
}
break;
}
t = null;
}
public void resume() {
isItOk = true;
t = new Thread(this);
t.start();
}
}
#Override
public boolean onTouch(View arg0, MotionEvent arg1) {
return false;
}
public static void damagePlayer() {
hpString = "Health " + playerHealth;
playerHealth = playerHealth - 5;
if(playerHealth<0){
hpString = "game over";
}
}
public static void setDirection(int i, int j) {
if(i == 0 && j == -1){
for(Sprite s: copy){
s.y++;
}
}
if(i == 0 && j == 1){
for(Sprite s: copy){
s.y--;
}
}
if(i == -1 && j == 0){
for(Sprite s: copy){
s.x++;
}
}
if(i == 1 && j == 0){
for(Sprite s: copy){
s.x--;
}
}
}
}
So the first part of my question is can anyone tell me why the for loops wont stop looping. And the second part of my question is how can I force the checkX and checkY methods to restart if the player selects a different point before he makes it to the first location.
Check and see if your problem lies with your signature in your checkX and checkY functions. Integer is a class, and int is a primitive type, so you can't do something like Integer n = 1, but you can do int n = 1.
public void checkX(int touchx)
public void checkY(int touchx)
So, I have a various Bitmaps on my canvas that are animated as individual classes.
When the Bitmap goes off screen i want the Bitmap to be removed from the linkedlist. they are in a linkedlist as there can be more than one of the same bitmap on the screen.
The bitmap is removing from the screen but it is still in the linkedlist as the linkedlist size is 1.
If i add more than one bitmap to the linkedlist they both get drawn but when on goes of the screen the game crashes.
Panel Class.
public void doDraw(long elapsed, long score, Canvas canvas) {
canvas.drawBitmap(background, 0, 0, null); // draw a black background
synchronized (mChimneys) {
if ((mPresents.size() < 3)) {
for (Present presents : mPresents) {
presents.doDraw(canvas);
}
}
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
synchronized (mChimneys) {
float x = event.getX();
float y = event.getY();
float mX = 0;
float mY = 0;
int mWidth = 0;
int mHeight = 0;
for (Santa santas : mSantas) {
mX = santas.getmX();
mY = santas.getmY();
mWidth = santas.getmBitmap().getWidth();
mHeight = santas.getmBitmap().getHeight();
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (x >= mX && x < (mX + mWidth) && y >= mY
&& y < (mY + mHeight)) {
if ((mPresents.size() < 4)) {
mPresents.add(new Present(getResources(), mX, mY));
mPresentNumber = mPresents.size();
}
}
}
}
return true;
}
public void animate(long elapsedTime) {
synchronized (mChimneys) {
for (Present presents : mPresents) {
boolean remove = presents.animate(elapsedTime);
if (remove) {
mPresents.remove(presents);
}
}
}
Present Class.
package com.droidnova.android;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
public class Present {
private float mX;
private float mY;
private Bitmap mBitmap;
private int mSpeedY;
Panel panel;
public Present(Resources res, float x, float y) {
Random rand = new Random();
List<Integer> my_presents = new LinkedList<Integer>();
my_presents.add(R.drawable.presentblue);
my_presents.add(R.drawable.presentpurple);
my_presents.add(R.drawable.presentred);
my_presents.add(R.drawable.presentyellow);
int choice = rand.nextInt(my_presents.size());
mBitmap = BitmapFactory.decodeResource(res, my_presents.get(choice));
mX = x + 50;
mY = y + 100;
mSpeedY = 5;
}
public void doDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, mX, mY, null);
}
public boolean animate(long elapsedTime) {
mY += mSpeedY * (elapsedTime / 20f);
boolean remove = checkBorders();
if (remove) {
return true;
}
return false;
}
private boolean checkBorders() {
if (mY + mBitmap.getHeight() >= Panel.mHeight) {
return true;
// mSpeedY = -mSpeedY;
// mY = Panel.mHeight - mBitmap.getHeight();
}
return false;
}
}
Any help is appreciated.
I think these lines may cause a problem:
for (Present presents : mPresents) {
boolean remove = presents.animate(elapsedTime);
if (remove) {
mPresents.remove(presents);
}
}
Since you are trying to remove "presents" from the "mPresents", while you are on that element of the list. I could be wrong, but I think this is a bit like trying to eat your own head. It might help to create another list (or just another Present object), and then remove that from the list after the loop is finished. For example, something like this:
ArrayList <Present> presentsToRemove = new ArrayList<Present>();
for (Present presents : mPresents) {
if (presents.animate(elapsedTime)) {
presentsToRemove.add(presents);
}
}
mPresents.removeAll(presentsToRemove);
Please let me know if this helps, or if I've misunderstood.