I have a database filled with records in the following format: . What I want my application to do is select records from an external database, and display those records on the phone screen using a SurfaceView.
Currently, I have an Activity, and a Service responsible for the record-gathering portion of the application. The Activity passes an intent to the Service, and the Service responds by returning the records that need to be displayed. The records are stored in my program as instances of the Data class, and merely have the screen-coordinates of where the element should be drawn in the View (I am just drawing a circle for every record in the DB).
For the sake of brevity, I won't include the service but I will include a skeleton of the Activity class and the Data that I wish to display.
public class Data{
private int x;
private int y;
private int id;
private int shape;
/*
* Getters and setters for class properties
*/
}
public class Displayer extends Activity {
int ht;
int wt;
dataReceiver dreceiver;
public static Map<String, Data> Info;
private LinearLayout linear;
private static final int RADIUS = 20;
Panel panel;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
panel=new Panel(this);
setContentView(panel);
Intent scv = new Intent(this, DataService.class);
startService(scv);
}
/*
* Various other methods to take care of onDestroy, onPause, etc.
*/
public class dataReceiver extends BroadcastReceiver {
//Get new data from intents sent by the Service and store it in Info variable
//Update the Testing Map data-structure in the Panel SurfaceView with new records
panel.setData(Info);
}
}
The problem that I am having pertains to the SurfaceView. I realize that many people are going to suggest that I use just a regular View, but my application involves a lot of elements, so a SurfaceView would be much more suitable for my needs. Below is a skeleton of my SurfaceView class that contains a nested class to manage the threads.
public class Panel extends SurfaceView implements SurfaceHolder.Callback {
private PanelThread _thread;
private Paint circlepaint;
private Paint textpaint;
private static int CircleColor = Color.YELLOW;
private static int TextColor = Color.RED;
private static Map<String, Data> Testing;
public Panel(Context context, Map<String, Data> entries) {
super(context);
getHolder().addCallback(this);
_thread = new PanelThread(getHolder(), this);
setFocusable(true);
textpaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textpaint.setStyle(Style.FILL_AND_STROKE);
textpaint.setColor(TextColor);
Testing = new HashMap<String, Data>();
// Log.d("TestingSize",String.valueOf(Testing.size()));
if (!(Testing.isEmpty())) {
Testing.clear();
}
for (Map.Entry<String, Data> e : entries.entrySet()) {
String keyvalue = e.getKey();
Data v = e.getValue();
Panel.Testing.put(keyvalue, v);
}
}
public Panel(Context context) {
super(context);
getHolder().addCallback(this);
_thread = new PanelThread(getHolder(), this);
setFocusable(true);
textpaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textpaint.setStyle(Style.FILL_AND_STROKE);
textpaint.setColor(TextColor);
Testing = new HashMap<String, Victims>();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
_thread.setRunning(true);
_thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
/* we have to tell thread to shut down
* & wait for it to finish,
* or else it might touch the Surface
* after we return and explode
*/
boolean retry = true;
_thread.setRunning(false);
while (retry) {
try {
_thread.join();
retry = false;
} catch (InterruptedException e) {
// we will try it again and again...
}
}
}
/*If new records are received
* from the service, they can be updated
* using this method
*/
public void setData(Map<String,Data>Info){
Testing=Info;
}
/*
* Iterate over all contents of Testing List and display them to the screen
*/
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!(Testing.isEmpty())) {
for (Map.Entry<String, Victims> e : Testing.entrySet()) {
Data d = e.getValue();
canvas.drawCircle(d.getX(), d.getY(), 10, circlepaint);
}
}
canvas.save();
canvas.restore();
}
/*
* Nested class to manage the threads for the SurfaceView
*/
class PanelThread extends Thread {
private SurfaceHolder _surfaceHolder;
private Panel _panel;
private boolean _run = false;
public PanelThread(SurfaceHolder surfaceHolder, Panel panel) {
_surfaceHolder = surfaceHolder;
_panel = panel;
}
public void setRunning(boolean run) {
_run = run;
}
public SurfaceHolder getSurfaceHolder() {
return _surfaceHolder;
}
#Override
public void run() {
Canvas c;
while (_run) {
c = null;
try {
c = _surfaceHolder.lockCanvas(null);
synchronized (_surfaceHolder) {
_panel.onDraw(c);
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
_surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}//end of PanelThread
}//end of Panel
The problem that I'm having is that once I make the initial call to the Panel class, I'm going to be getting new records from the service, and consequently, my Info Map data-structure is going to get updated. However, my Panel class just gets stuck in a loop and never receives any new Data objects. All examples of SurfaceViews I've found have involved updating them methods within the SurfaceView class itself (e.g. touching the screen and creating a new image, etc.) Sadly, I'm stumped on this particular problem. Is there a better approach for designing my Activity/SurfaceView interaction? Is an additional View required?
From a quick look at your code I think the problem is that you are trying to update a data structure from the UI thread while acting on it in the PanelThread. It also looks like your Activity class and Panel are also out of sync - I don't see the setData method in your Panel class, but I assume it updates the Testing static member variable.
My suggestions:
1 - Testing should not be static - it's fundamentally associated with this instance of the class.
2 - Use Synchronize when touching the data (Testing). This should be done for both setting and reading the data.
3 - Put a sleep or a wait call in your run loop - this gives time for the activity UI thread to update Testing. Also saves battery and allows you to pick a frame rate for updates.
Related
I'm making Tower Defence Game using JavaFX, my current problem is updating view after animation calculations. I've tried making new Thread and starting it, but whenever i touch GraphicContext from Canvas, game crashed. My current game loop class looks like:
import model.Enemy;
import model.Map;
import model.Player;
import model.Model;
import view.View;
public class GameLoop2 {
private Canvas canvas;
public Integer enemiesNr;
public Integer enemiesSpawnTime;
public Integer spawnedEnemies;
private long lastSpawn;
private ArrayList<Enemy> enemies;
private boolean firstPlay;
private Model model;
private Map map;
public GameLoop2(Canvas mainCanvas, Model m) {
model = m;
map = model.getMap();
canvas = mainCanvas;
enemiesNr = map.getEnemiesNr();
enemiesSpawnTime = model.getMap().getEnemiesSpawnTime();
spawnedEnemies = 0;
lastSpawn = System.currentTimeMillis();
enemies = new ArrayList<>(enemiesNr);
for(int i=0; i < enemiesNr;i++)
enemies.add(i, new Enemy(map.getStartXPosition(), map.getStartXPosition()));
}
private void spawnEnemy() {
if(spawnedEnemies >= enemiesNr)
return;
enemies.get(spawnedEnemies).setAlive(true);
View.drawEnemy(canvas, enemies.get(spawnedEnemies));
spawnedEnemies++;
lastSpawn = System.currentTimeMillis();
}
private void enemyPhysics() {
for(Enemy e: enemies)
if(e.isAlive())
e.physics(1);
}
private void drawEnemies(){
for(Enemy e: enemies)
if(e.isAlive())
View.drawEnemy(canvas,e);
}
private void update() {
canvas.getGraphicsContext2D().restore();
View.drawMap(map, canvas);
drawEnemies();
}
public void start() {
while(true){
// Calculations
long now = System.currentTimeMillis();
if (now - lastSpawn > enemiesSpawnTime) {
spawnEnemy();
}
enemyPhysics();
// Updating View
update();
// View is refreshed after break; statement
if(now - lastSpawn > 6000)
break;
}
}
I've also tried Service class, but it didn't worked for me. I would also want to make claculation as fast as possible to make movement animation effect for aproaching enemies. It will be nice to make it in a way that will allow to add another thread, for example background music, or calculating damage to main tower.
I've got myself a mediocre game going for myself. It has a main menu activity that when you click play starts a new game play activity. When you lose the game you can either retry or go back to the main menu. the back button on the gameplay activity is just a ractangle with this coding.
if (GlobalVariables.GAMEOVER && backButton.contains((int)(event.getX()), (int)(event.getY())))
{
Intent backButtonIntent = new Intent(context, MainMenuActivity.class);
context.startActivity(backButtonIntent);
}
The program does not crash and it does switch to the mainmenu activity but it is frozen and no buttons are clickable. and here is the error i get.
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Canvas.drawColor(int, android.graphics.PorterDuff$Mode)' on a null object reference
at android.view.SurfaceView.dispatchDraw(SurfaceView.java:451)
at android.view.View.draw(View.java:17472)
at android.view.SurfaceView.draw(SurfaceView.java:442)
at com.example.jordanschanzenbach.myapplication.GamePanel.draw(GamePanel.java:84)
at com.example.jordanschanzenbach.myapplication.MainThread.run(MainThread.java:52)
here is the code that it is pointing to in the game panel. "super.draw(canvas)"
public void draw(Canvas canvas)
{
super.draw(canvas);
backGround.draw(canvas);
gameFloor.draw(canvas);
gamePlayScene.draw(canvas);
}
i understand that this is a nullpointerexception and a duplicate question. but i dont understand why when i switch to my main menu activity it is causing this error when the game panel isn't even linked to the main menu activity. Please help a young student :) thank you and if you would like to see any other code i will edit this post immediately.
Check if the thread responsible for calling the draw method was left running before starting another activity. I'm not quite sure but it probably still tries to render on the SurfaceView even if it has already been detached from the window.
Edit:
I was able to replicate your problem. The thread that handles the drawing does not always instantly get the updated value of the variable you use as a while loop condition. You can choose to perform synchronization and/or other threading stuff, but you can simply try to break the while loop like how I did in the code below. Also, try to stop your drawing thread by making your while loop condition false inside the SurfaceHolder.Callback#surfaceDestroyed(SurfaceHolder).
public class GamePanel extends SurfaceView implements SurfaceHolder.Callback {
private TextPaint paint;
private volatile boolean isDrawing;
private DrawingThread drawingThread;
public GamePanel(Context context) {
super(context);
initialize();
}
// omitted the other constructors for brevity
private void initialize() {
...
drawingThread = new DrawingThread();
// do this so that you can get an surface updates
getHolder().addCallback(this);
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
canvas.drawText("Hello, world", 20, 20, paint);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
isDrawing = true;
drawingThread.start();
// start drawing only after the surface was created
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { }
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
isDrawing = false;
// try to stop the drawing thread when the surface
// is destroyed
}
private class DrawingThread extends Thread {
#Override
public void run() {
while (isDrawing) {
try {
Canvas canvas = getHolder().lockCanvas();
// check if canvas is null just to make sure
if (canvas == null) break;
draw(canvas);
getHolder().unlockCanvasAndPost(canvas);
} catch (IllegalArgumentException e) {
// This is thrown by SurfaceHolder#lockCanvas().
// Break when the surface has already been destroyed
// but the variable `isDrawing` is still true.
break;
}
}
}
}
}
I want to develop a simple interactive game (like arcanoid). I already have implemented a menu and different views, and now I need to develop the actually game (draw flying ball, some movable platform) and I don't know how to do this. I need something like canvas where I can draw my graphic each frame.
I have tryed to implement this with Canvas and Timer. But it doesn't want update graphic itself, but only when user clicks on screen or similar. Also I saw com.google.gwt.canvas.client.Canvas, but I cannot understand how to use it in Vaadin application.
So my question is next: is it possible to draw some graphic each frame with high framerate in any way? If possible, how can I do this?
P.S. I use the Vaadin 7.3.3.
ADDED LATER:
Here is a link to my educational project with implementation below.
I'll be glad if it helps someone.
ORIGINAL ANSWER:
Well... I found the solution myself. First of all, I have created my own widget - "client side" component (according to this article).
Client side part:
public class GWTMyCanvasWidget extends Composite {
public static final String CLASSNAME = "mycomponent";
private static final int FRAMERATE = 30;
public GWTMyCanvasWidget() {
canvas = Canvas.createIfSupported();
initWidget(canvas);
setStyleName(CLASSNAME);
}
Connector:
#Connect(MyCanvas.class)
public class MyCanvasConnector extends AbstractComponentConnector {
#Override
public Widget getWidget() {
return (GWTMyCanvasWidget) super.getWidget();
}
#Override
protected Widget createWidget() {
return GWT.create(GWTMyCanvasWidget.class);
}
}
Server side part:
public class MyCanvas extends AbstractComponent {
#Override
public MyCanvasState getState() {
return (MyCanvasState) super.getState();
}
}
Then I just add MyCanvas component on my View:
private void createCanvas() {
MyCanvas canvas = new MyCanvas();
addComponent(canvas);
canvas.setSizeFull();
}
And now I can draw anything on Canvas (on client side in GWTMyCanvasWidget) with great performance =). Example:
public class GWTMyCanvasWidget extends Composite {
public static final String CLASSNAME = "mycomponent";
private static final int FRAMERATE = 30;
private Canvas canvas;
private Platform platform;
private int textX;
public GWTMyCanvasWidget() {
canvas = Canvas.createIfSupported();
canvas.addMouseMoveHandler(new MouseMoveHandler() {
#Override
public void onMouseMove(MouseMoveEvent event) {
if (platform != null) {
platform.setCenterX(event.getX());
}
}
});
initWidget(canvas);
Window.addResizeHandler(new ResizeHandler() {
#Override
public void onResize(ResizeEvent resizeEvent) {
resizeCanvas(resizeEvent.getWidth(), resizeEvent.getHeight());
}
});
initGameTimer();
resizeCanvas(Window.getClientWidth(), Window.getClientHeight());
setStyleName(CLASSNAME);
platform = createPlatform();
}
private void resizeCanvas(int width, int height) {
canvas.setWidth(width + "px");
canvas.setCoordinateSpaceWidth(width);
canvas.setHeight(height + "px");
canvas.setCoordinateSpaceHeight(height);
}
private void initGameTimer() {
Timer timer = new Timer() {
#Override
public void run() {
drawCanvas();
}
};
timer.scheduleRepeating(1000 / FRAMERATE);
}
private void drawCanvas() {
canvas.getContext2d().clearRect(0, 0, canvas.getCoordinateSpaceWidth(), canvas.getCoordinateSpaceHeight());
drawPlatform();
}
private Platform createPlatform() {
Platform platform = new Platform();
platform.setY(Window.getClientHeight());
return platform;
}
private void drawPlatform() {
canvas.getContext2d().fillRect(platform.getCenterX() - platform.getWidth() / 2, platform.getY() - 100, platform.getWidth(), platform.getHeight());
}
}
i been working on a game with no bitmaps or anything, I'm using rectangles as objects and changing their color for their purpose like a red rectangles for player and gray rectangles for walls. My question is what is the right way to replace the rectangles with bitmaps/images?
I know to load Bitmaps you can just do this :
Bitmap randomBitmap = BitmapFactory.decodeResource(getResources(),
com.example.android4gametest.R.drawable.ic_launcher);
Should i load all my Bitmaps and pass them to their Classes or should i load the bitmap inside their class instead of passing it ? and how would i do that because i cannot use the BitmapFactory because i have no access to the getResources()! or should i load my bitmaps/images from my assets folder which i know i wont have the same "tools" you can say to mess with the bitmap.
MainActivity
public class MainActivity extends Activity {
Game theGame;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(new Game(this));
}
}
The Game Panel
public class Game extends SurfaceView implements SurfaceHolder.Callback {
GameThread _thread;
public Game(Context context) {
super(context);
getHolder().addCallback(this);
setFocusable(true);
_thread = new GameThread(getHolder(), this);
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
public void surfaceCreated(SurfaceHolder holder) {
_thread.setRunning(true);
_thread.start();
}
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
#Override
protected void onDraw(Canvas canvas) {
Log.d("OnDraw", "it is Drawing");
canvas.drawColor(Color.BLUE);
}
public void update() {
// TODO Auto-generated method stub
}
}
GameLoop Nothing here
public class GameThread extends Thread{
/*FPS Code*/
private final static int MAX_FPS = 30;
private static final int FRAME_PERIOD = 1000/MAX_FPS;
protected SurfaceHolder holder;
protected Game game;
private boolean isRunning = false;
public GameThread(SurfaceHolder _holder, Game _game) {
this.holder = _holder;
this.game = _game;
}
/**
* Returns True if the game is still running and False if the game is over
* #return
*/
public boolean isRunning() {
return isRunning;
}
/**
* Set to true for the game loop to start
* #param isRunning
*/
public void setRunning(boolean isRunning) {
this.isRunning = isRunning;
}
#Override
public void run() {
Canvas c;
Log.d("Pallyways", "Starting game Loop");
long beingTime;
long timeDiff;
int sleepTime;
int framesSkipped;
sleepTime = 0;
while(isRunning){
c = null;
try{
c = holder.lockCanvas();
synchronized(holder){
beingTime = System.currentTimeMillis();
framesSkipped = 0;
game.update();//Update
game.onDraw(c);//Redraw
timeDiff = System.currentTimeMillis() - beingTime ;
sleepTime = (int) (FRAME_PERIOD - timeDiff);
if(sleepTime>0){
try{
Thread.sleep(sleepTime);}
catch (InterruptedException e) {
e.printStackTrace();}
finally{}
}
while(sleepTime<0 && framesSkipped < 5){
game.update();
sleepTime+= FRAME_PERIOD;
framesSkipped++;
}
}
}finally{if(c!=null){
holder.unlockCanvasAndPost(c);
}
}
}
}
}
Hero Class I have not started but i would like to know how to load a bitmap on a class that is on a different Package
package com.example.android4gametest.Actors;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import com.example.android4gametest.R;
public class Hero {
//getContext() gives me an error and that is because it does not have a reference
private Bitmap hero = BitmapFactory.decodeResource(getContext().getResources(),
R.drawable.ic_launcher);
public Hero(){
}}
Depending on the sizes/number of bitmaps, it may be fine to load them in the constructor of your Game class. Be aware though, if you're loading too many/too big bitmaps into memory at once, you may run into some OOM errors or a lag in when your first draw call gets made. You have to be sure you're nulling, recycling your bitmaps efficiently when you no longer need them. Struggled with this issue with my first game
Do not load the Bitmap in your game object's constructors. If any of those Bitmaps would be used by multiple objects (i.e. an enemy class), then if you loaded them in your object's constructors you would have multiple copies of the Bitmap in memory. This could lead to an OutOfMemoryException. Instead load your Bitmap outside of your constructors (in your game thread or game panel) and then pass the Bitmap objects to the constructors or setters and set your private Bitmaps that way. You'll only have to keep a limited number of Bitmaps in memory.
You may also want to consider a loading "screen" which displays a progress bar or spinner while you load those Bitmaps.
Where you load is not important. I think your main issue is accessing the resources. In a view, you can call:
getContext().getResources()
Edit:
So in the method signature of the constructor of Hero add Context as a parameter:
public Hero(Context context){
private Bitmap hero = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher);
}
DO NOT! use dynamic objects.. I mean Do not create dynamic objects in a game at all, these will trigger the GC and make everything stutter when it's running whenever it wants. You must take control over everything yourself and use static/final "objects" and arrays on the obstacles and enemies. The objects will be loaded on to the heap at startup, which means that you will have them loaded until the app is shut down. Therefore you must find the balance between what to have in these static objects and not, but it's easy when you have found it.
The bitmaps should be loaded outside these static non-dynamic objects, the objects should only do the calculations.
And never use any unnessesary calculations, loops or Thread.sleep in between your
c = holder.lockCanvas(); and holder.unlockCanvasAndPost(c); like you're doing in your code.
This can and will finally make the game flicker.
Regards,
Me
does anyone knows how to use the blackberry JDE API to create a screen slide animation similar to Featured Items screen in the Blackberry App World? I am aware that in blackberry 5.0, there are some transition apis to perform that. But I am looking to do it for version 4.6 OS. It has the nice scrolling effect using the scrolling ball in blackberry bold.
Thanks.
As an alternative to the screenshot/Bitmap approach...
In the paint method of your screen you can use Graphics.pushContext(..) to push a clipping region and drawing offset. For best results, you'll want to do the transition in a runnable, and synchronize on the event lock. This will ensure that your screen can be dismissed in the middle of a transition.
Rough example:
class TransitionScreen extends Screen {
private int transitionOffset;
private boolean isTransitionHorizontal;
private ScreenTransition currentTransition;
public TransitionScreen(boolean isTransitionHorizontal) {
this.isTransitionHorizontal = isTransitionHorizontal;
transitionOffset = getTransitionMaximum(); // So the screen starts offset
}
protected void paint(Graphics graphics) {
// use transitionOffset as x or y depending on isTransitionHorizontal
graphics.pushContext(...);
}
protected void onExposed() {
transitionToOffset(0);
}
protected void onObscured() {
int target = getTransitionMaximum();
transitionToOffset(target);
}
private int getTransitionMaximum() {
return isTransitionHorizontal ? Display.getWidth() : Display.getHeight();
}
private void transitionToOffset(int target) {
if (currentTransition != null) {
currentTransition.stop();
}
currentTransition = new ScreenTransition(target);
getApplication().invokeLater(currentTransition);
}
}
class ScreenTransition implements Runnable {
private boolean animating;
private int target;
public ScreenTransitionUpdater(int target) {
this.target = target;
}
public void stop() {
animating = false;
}
public void run() {
while(animating) {
Object eventLock = getApplication().getEventLock();
synchronized(eventLock) {
// Interpolate myOffset to target
// Set animating = false if myOffset = target
invalidate();
}
}
}
}
No need to mark animating as volatile as it is ignored on this platform.
Maybe use a timer to change the coordinate position of the images in the paint method