I'm going to create a moving circle for my later project, and the circle will keep moving, and it interior color will change like color emitting , the changing color will from little circle to larger circle in 5 levels, so how to keep each color change to stay a while and I hope these code present with thread, so I create two thread for the purpose, one control circle moving, another control the circle's interior color emit
here is my code:
import java.awt.*;
import static java.awt.Color.black;
import static java.awt.Color.yellow;
import static java.awt.FlowLayout.RIGHT;
import java.awt.event.*;
import java.awt.geom.Arc2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import static java.lang.Math.abs;
import java.util.Random;
import javax.swing.*;
import java.util.concurrent.ExecutorService;
class thepane extends JPanel implements Runnable{
public float x,y,r;
public float speedx,speedy;
thepane(float lx,float ly,float lr, float sx,float sy){
loadspeed(sx,sy);
load(lx,ly,lr);
for(int i=0;i<5;i++)
fc[i]=new Color(nd.nextInt(255),nd.nextInt(255),nd.nextInt(255));
}
public void load(float lx,float ly,float lr){
x=lx;y=ly;r=lr;
}
public void loadspeed(float sx,float sy){
speedx=sx;speedy=sy;
}
public void xmoving(){
x+=speedx;
}
public void ymoving(){
y-=speedy;
}
public void touchbond(){
if(x>getWidth()-r||x<0)
speedx*=-1;
if(y>getHeight()-r||y<0)
speedy*=-1;
if(x>getWidth()-r)
x=getWidth()-r;
else if(x<0)
x=0;
if(y>getHeight()-r)
y=getHeight()-r;
else if(y<0)
y=0;
}
Random nd=new Random();
int colorcount=0;
int emitcount=0;
boolean emit=false;
Color[] fc=new Color[5];
Graphics2D comp2D ;
Thread athread;
#Override
public void paintComponent(Graphics comp) {
comp2D = (Graphics2D) comp;
//create rectangle background
comp2D.setColor(Color.BLACK);
comp2D.fillRect(0, 0, getWidth(), getHeight());
//set information text
comp2D.setFont( new Font("Arial", Font.BOLD, 12));
comp2D.setColor(Color.WHITE);
comp2D.drawString("Centre("+(x+r/2)+' '+(y+r/2)+"), xspeed: "+speedx+" yspeed: "+speedy, 10f,10f );
comp2D.drawString("panel width "+getWidth()+" panel height "+getHeight()+" circle radius "
+r, 10f, 22f);
}
//thread run()
#Override
public void run() {
x=100;y=100;
System.out.println("thread in pane start!!!! (current colorcount = "+colorcount+')');
while(true){
circleEmit(fc[colorcount%5]);
repaint();
sleeping(1);
// comp2D=(Graphics2D)this.getGraphics();
// colorEmit(comp2D);
}
}
//wait method
public void waiting(){
try{wait();}
catch(Exception e){}}
public void waiting2D(int time){
try{comp2D.wait(time);}
catch(Exception e){}
}
public void waiting(int time){
try{wait(time);}
catch(Exception e){}
}
//sleep method
public void sleeping(int n){
try{
Thread.sleep(n);
}catch(Exception f){
System.out.print(f);
}
}
Ellipse2D.Float[] e=new Ellipse2D.Float[5];
public void loade(){
float centrex=x+r/2,centrey=y+r/2;
e[0]= new Ellipse2D.Float(centrex-r/10, centrey-r/10, r/5, r/5);
e[1]= new Ellipse2D.Float(centrex-r/5, centrey-r/5, 2*r/5, 2*r/5);
e[2]= new Ellipse2D.Float(centrex-3*r/10, centrey-3*r/10, 3*r/5, 3*r/5);
e[3]= new Ellipse2D.Float(centrex-2*r/5, centrey-2*r/5, 4*r/5, 4*r/5);
e[4]= new Ellipse2D.Float(centrex-r/2, centrey-r/2, r, r);
}
public Color ff;
public synchronized void circleEmit(Color fc){
comp2D=(Graphics2D)this.getGraphics();
loade();
comp2D.setColor(fc);
comp2D.fill(e[emitcount%5]);
waiting(5);
emitcount++;
}
public synchronized void callnotify(){
this.notify();
}
//iterative way to generate color emit
public void colorEmit(Graphics2D comp2D){
//create circle
//set circle property
float centrex=x+r/2,centrey=y+r/2;//so x=centrex-r/2;y=centrey+r/2
Ellipse2D.Float e1 = new Ellipse2D.Float(centrex-r/10, centrey-r/10, r/5, r/5);
Ellipse2D.Float e2 = new Ellipse2D.Float(centrex-r/5, centrey-r/5, 2*r/5, 2*r/5);
Ellipse2D.Float e3 = new Ellipse2D.Float(centrex-3*r/10, centrey-3*r/10, 3*r/5, 3*r/5);
Ellipse2D.Float e4 = new Ellipse2D.Float(centrex-2*r/5, centrey-2*r/5, 4*r/5, 4*r/5);
Ellipse2D.Float e5 = new Ellipse2D.Float(centrex-r/2, centrey-r/2, r, r);
if(colorcount>=4)
emit(comp2D,fc[(colorcount-4)%5],e5);
waiting(1000);
if(colorcount>=3)
emit(comp2D,fc[(colorcount-3)%5],e4);
waiting(1000);
if(colorcount>=2)
emit(comp2D,fc[(colorcount-2)%5],e3);
waiting(1000);
if(colorcount>=1)
emit(comp2D,fc[(colorcount-1)%5],e2);
waiting(1000);
emit(comp2D,fc[colorcount%5],e1);
waiting(1000);
colorcount++;
}
private void emit(Graphics2D comp,Color thecolor,Ellipse2D.Float f){
comp.setColor(thecolor);
comp.fill(f);
}
}
//------------------------------------------------------------------------------------
//main class
public class drawpanel extends Thread implements ActionListener{
JFrame frame=new JFrame();
thepane panel;
JButton FlyingBalls=new JButton("balls"),exit=new JButton("Exit"),stop=new JButton("Stop");
JButton slow=new JButton("slow down"),resume=new JButton("resume");
Float x,y,r;
public void sleeping(int n){
try{
Thread.sleep(n);
}catch(Exception f){
System.out.print(f);
}
}
Thread newthread,pthread;
Thread[] five=new Thread[5];
drawpanel(){
frame.setTitle("FlyingBalls");
frame.setLocation(100, 100);
frame.setLayout(null);
//x,y,r,speedx,speedy
panel=new thepane(nd.nextInt(800),nd.nextInt(500),40,nd.nextFloat()*20+1,nd.nextFloat()*10+1);
panel.setSize(800,500);
frame.setSize(810,580);
frame.add(panel);
FlyingBalls.setSize(80,30);exit.setSize(70,30);stop.setSize(70,30);slow.setSize(140,30);
resume.setSize(100,30);
FlyingBalls.addActionListener(this);
exit.addActionListener(this);
stop.addActionListener(this);slow.addActionListener(this);resume.addActionListener(this);
frame.add(FlyingBalls);frame.add(exit); frame.add(stop);frame.add(slow);frame.add(resume);
FlyingBalls.setLocation(20,500);exit.setLocation(190, 500);stop.setLocation(110,500);
slow.setLocation(270,500);resume.setLocation(420,500);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//control moving ball
newthread=new Thread(this);
//control color change
for(int i=0;i<5;i++){
five[i]=new Thread(panel);
}
// newthread.start();
panel.colorcount++;
five[0].start();
panel.colorcount=2;
// five[1].start();
panel.waiting(5);
}
public static void main(String[] arg){
drawpanel apanel=new drawpanel();
}
int bw=800,bh=500;
void setp(){
x=panel.x;y=panel.y;
}
void touchbond(){
System.out.println("width:"+panel.getWidth()+"Height:"+panel.getHeight());
System.out.println("xposition:"+x+"yposition:"+y);
if(x+r>panel.getWidth()){
panel.speedx*=-1;
x=bw-r;
}
else if(x-r<0){
panel.speedx*=-1;
x=r;
}
if(y-r<0){
panel.speedy*=-1;
y=r;
}
else if(y+r>panel.getHeight()){
panel.speedy*=-1;
y=bh-r;
}
panel.x=x;panel.y=y;
}
int T=10;
Random nd=new Random();
#Override
public void run(){
r=panel.r;
panel.loadspeed(-6.33f,-3.4f);
while(true){
if(stopcount==0){//button control variable
panel.xmoving();panel.ymoving();
panel.touchbond();
sleeping(T);}
panel.loade();
// panel.callnotify();
// panel.colorEmit(panel.comp2D);
panel.repaint();
}
}
#Override
public void start(){
}
int count=0,stopcount=0;
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==exit){
System.exit(0);
}
if(e.getSource()==FlyingBalls){
//panel=new thepane();
}
if(e.getSource()==resume){
stopcount=0;T=10;
panel.emit=false;
}
if(e.getSource()==slow){
if(count%2==0)
T=500;
else
T=10;
count++;
}
if(e.getSource()==stop){
stopcount++;
panel.emit=true;
}
}
}
So, lots of theory to cover.
Firstly...
Animation is not easy, good animation is hard.
Swing is single threaded and is not thread safe
This means that you should not perform any long running or blocking operations within the context of the Event Dispatching Thread.
It also means that you shouldn't modify the UI or anything the UI relies on from outside the context of the Event Dispatching Thread
More threads != more work
More threads doesn't always mean you're going to get more done. In fact, in this scenario, it could really cause a huge number of issues, as you need the ability to reason out the state at a single point in time (when painting)
Animation Theory
Okay, animation is simply the illusion of change, how you accomplish that will come down to the problem you trying to solve.
For me, the best animations are time based animations, not linear.
A linear animation keeps updating from its start state till it reaches its end state, in a constant progression. These don't tend to scale well and can suffer issues on low performant systems.
A time based animation is one where the amount of time is defined and then, based on a anchor time (ie start time) and the state of the animation is updated based on the amount of time which is passed. This is a really simple way to achieve "frame dropping". You'd also be very surprised to find that in general terms, time based animations tend to look better across more platforms.
A time based animation is also more capable of generating "easement" effects, but that's getting way deeper then we need to go right now.
Okay, but what's this got to do with your problem? Well, actually, quite a bit.
The first thing we need is some kind of "main-loop" from which all the animation can be driven. Typically, I'd look to a good animation library, but failing that, a simple Swing Timer will do the basic good really well.
It generates its ticks in the Event Dispatching Thread, which makes it very useful for our needs. See How to Use Swing Timers for more details
So, we start with something like...
private Timer timer;
//...
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// Update the state
repaint();
}
});
//...
timer.start();
This gives us our "main loop", from which we can update the state as needed and then trigger a repaint of the component.
For the purpose of this demonstration, I'm going to devise a self-contained unit of "animation duration", used to track the amount of time which has passed since the animation was started, this is personal choice, but it would allow me to drive a number of animations and it contains the logic to a single unit of work.
public class AnimationDuration {
private Duration duration;
private Instant startedAt;
public AnimationDuration(Duration duration) {
this.duration = duration;
}
public Duration getDuration() {
return duration;
}
public void start() {
startedAt = Instant.now();
}
public void stop() {
startedAt = null;
}
public boolean isRunning() {
return startedAt != null;
}
public float getProgress() {
Duration runningTime = Duration.between(startedAt, Instant.now());
if (runningTime.compareTo(duration) > 0) {
runningTime = duration;
}
long total = duration.toMillis();
float progress = runningTime.toMillis() / (float) total;
return progress;
}
}
This basically allows to trigger the animation to start running (anchor point in time) and then get the progress of the animation at any point in time. This provides a normalised concept from 0-1, so if we want to make it longer or shorter, all we do is adjust the duration and everything else is taken care of.
For your specific problem, I'd consider some kind of "time line" or "key frames", which defines that certain actions should occur at certain points of time along the time line.
Now, the following is a really simple concept, but it gets the job.
public interface KeyFrame {
public float getProgress();
}
public class TimeLine<K extends KeyFrame> {
private List<K> keyFrames;
public TimeLine() {
keyFrames = new ArrayList<>(25);
}
// Returns the key frames between the current progression
public K getKeyFrameAt(float progress) {
for (int index = 0; index < keyFrames.size(); index++) {
K keyFrame = keyFrames.get(index);
if (progress >= keyFrame.getProgress()) {
if (index + 1 < keyFrames.size()) {
K nextFrame = keyFrames.get(index + 1);
// But only if your between each other
if (progress < nextFrame.getProgress()) {
return keyFrame;
}
} else {
// Nothing after me :D
return keyFrame;
}
}
}
return null;
}
public void add(K keyFrame) {
keyFrames.add(keyFrame);
Collections.sort(keyFrames, new Comparator<KeyFrame>() {
#Override
public int compare(KeyFrame lhs, KeyFrame rhs) {
if (lhs.getProgress() > rhs.getProgress()) {
return 1;
} else if (lhs.getProgress() < rhs.getProgress()) {
return -1;
}
return 0;
}
});
}
}
This allows you to define certain KeyFrames along the timeline, based on a normalised concept of time and then provides the ability to get the KeyFrame based on the current progression through animation.
There are much more complex solutions you might consider, which would generate self contained events based on time progressions automatically, but I prefer been able to driver the animation itself independently, makes these types of things more flexible - add a JSlider and you can manipulate the progression manually ;)
The next thing we need is something to carry the properties for the circle KeyFrame ...
public class CirclePropertiesKeyFrame implements KeyFrame {
private float progress;
private double radius;
private Color color;
public CirclePropertiesKeyFrame(float progress, double radius, Color color) {
this.progress = progress;
this.radius = radius;
this.color = color;
}
#Override
public float getProgress() {
return progress;
}
public Color getColor() {
return color;
}
public double getRadius() {
return radius;
}
#Override
public String toString() {
return "KeyFrame progress = " + getProgress() + "; raidus= " + radius + "; color = " + color;
}
}
Now, we need to put it together...
public class TestPane extends JPanel {
private AnimationDuration timelineDuration;
private TimeLine<CirclePropertiesKeyFrame> timeLine;
private Timer timer;
private CirclePropertiesKeyFrame circleProperties;
public TestPane() {
timelineDuration = new AnimationDuration(Duration.ofSeconds(10));
timeLine = new TimeLine<>();
timeLine.add(new CirclePropertiesKeyFrame(0, 5, Color.CYAN));
timeLine.add(new CirclePropertiesKeyFrame(0.2f, 10, Color.BLUE));
timeLine.add(new CirclePropertiesKeyFrame(0.4f, 15, Color.GREEN));
timeLine.add(new CirclePropertiesKeyFrame(0.6f, 20, Color.YELLOW));
timeLine.add(new CirclePropertiesKeyFrame(0.8f, 25, Color.MAGENTA));
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (timelineDuration.isRunning()) {
float progress = timelineDuration.getProgress();
if (progress >= 1.0) {
timelineDuration.stop();
}
CirclePropertiesKeyFrame keyFrame = timeLine.getKeyFrameAt(progress);
circleProperties = keyFrame;
}
repaint();
}
});
}
#Override
public void addNotify() {
super.addNotify();
timelineDuration.start();
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
timer.stop();
timelineDuration.stop();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (circleProperties != null) {
double radius = circleProperties.radius;
double xPos = (getWidth() / 2) - radius;
double yPos = (getHeight() / 2) - radius;
g2d.setColor(circleProperties.color);
g2d.fill(new Ellipse2D.Double(xPos, yPos, radius * 2, radius * 2));
}
g2d.dispose();
}
}
And then we end up with something like...
Now, this is a 10 second sequence, so every 2 seconds it will update. Try changing the duration of the AnimationDuration and see what happens.
Note This is a non-repeating animation (it doesn't loop). You could make it loop, but the calculation to do so becomes more complicate, as you need to consider by how much you're over the expected Duration and then apply that to the next cycle, so it looks smooth
But what about movement?
Well, actually, pretty much already answered that question. You would also place the movement code inside the Timers ActionListener, right before the repaint request. In fact, I might be tempted to create some kind of class that could take the current KeyFrame information and combine it with the location properties, this would then be used the paintComponent method to draw the circle.
I want to blend the animation states ...
Well, that's a much more difficult question, especially when it comes to colors.
The basic theory is, you need the two key frames which set either side of the current progression. You would then apply a "blending" algorithm to calculate the amount of change to be applied between the two key frames.
Not impossible, just a step more difficult
How to close new frame without exiting whole application?
What is easiest way to do it by clicking X button.
Thanks in advance.
ControlFrame cf;
void setup()
{
cf = new ControlFrame(this,500,500, "cf name");
}
class ControlFrame extends PApplet
{
int w,h;
PApplet parent;
public ControlFrame(PApplet _parent, int _w, int _h, String _name)
{
super();
parent=_parent; w=_w; h=_h;
PApplet.runSketch(new String[]{this.getClass().getName()},this);
}
public void settings()
{ size(w,h); }
}
Note: Your question doesn't really have anything to do with ControlP5.
Step 1: Get a reference to the native window. How you do this depends on the renderer you're using. If you're using the default renderer, it looks like this:
Frame frame = ( (SmoothCanvas) ((PSurfaceAWT)surface).getNative()).getFrame();
Step 2: You can then call dispose() on that Frame to hide it without quititng the entire application.
frame.dispose();
Putting it all together, it looks like this:
import java.awt.Frame;
import processing.awt.PSurfaceAWT;
import processing.awt.PSurfaceAWT.SmoothCanvas;
ControlFrame cf;
void setup()
{
cf = new ControlFrame(this, 500, 500, "cf name");
}
class ControlFrame extends PApplet
{
int w, h;
PApplet parent;
public ControlFrame(PApplet _parent, int _w, int _h, String _name)
{
super();
parent=_parent;
w=_w;
h=_h;
PApplet.runSketch(new String[]{this.getClass().getName()}, this);
}
public void settings()
{
size(w, h);
}
public void draw(){
//needed for mousePressed
println(millis());
}
public void mousePressed(){
Frame frame = ( (SmoothCanvas) ((PSurfaceAWT)surface).getNative()).getFrame();
frame.dispose();
}
}
Step 3: Note that your second sketch will continue running, so you might also want to call noLoop() to prevent unnecessary computation.
I am writing an game in java with Libgdx and have a question about how to have multiple instances of the same object in an ArrayList without abusing the garbage collector.
this is the code of my main game state. I am still really new, so I assume my coding habits are downright awful:
package com.frog.game;
import java.util.ArrayList;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.badlogic.gdx.math.MathUtils;
import com.frog.entities.Frog;
import com.frog.entities.Grass;
import com.frog.entities.LilyPad;
import com.frog.gamestates.GameState;
import com.frog.managers.GameStateManager;
import com.frog.managers.Jukebox;
import com.frog.managers.Save;
import com.frog.managers.SimpleDirectionGestureDetector;
import com.frog.managers.SpeedManager;
import com.frog.game.Game;
public class PlayState extends GameState {
private SpriteBatch sb;
private Frog frog;
private BitmapFont font;
private ArrayList<LilyPad> lilypads;
private Grass grass;
private float hopY;
public boolean tmp;
private SpeedManager speedManager;
private float speed;
private float[] placement;
public PlayState(GameStateManager gsm) {
super(gsm);
}
#SuppressWarnings("deprecation")
public void init() {
speedManager = new SpeedManager();
lilypads = new ArrayList<LilyPad>();
sb = new SpriteBatch();
frog = new Frog();
frog.init();
grass = new Grass();
grass.init();
hopY = Game.SIZE * 0.8f;
placement = new float[] {
0,
Game.SIZE,
Game.SIZE * 2
};
addPad();
FreeTypeFontGenerator gen = new FreeTypeFontGenerator(
Gdx.files.internal("PressStart2P-Regular.ttf")
);
font = gen.generateFont((Game.WIDTH / 10));
gen.dispose();
Gdx.input.setInputProcessor(new SimpleDirectionGestureDetector(new SimpleDirectionGestureDetector.DirectionListener() {
#Override
public void onUp() {
// TODO Auto-generated method stub
}
#Override
public void onRight() {
frog.moveRight();
}
#Override
public void onLeft() {
frog.moveLeft();
}
#Override
public void onDown() {
}
}));
}
This is the method I use to add another lilypad at the top of the screen. I make it appear in one of three places randomly. Since the main function of these lilypads is to scroll down the screen, instances of the lilypad are being added and removed from the array non stop. I know this kills because each time I add a new lilypad, I have to run init() for it, otherwise I will get a nullpointerexception. init() instantiates a bunch of objects in order to make that lilypad render, such as textures, the default Y value etc. Is there any way I could run the init() method once for the entire arraylist, even though I am constantly adding & removing them? I have considered wrapping the same lilypads around the screen when they hit the bottom, so I would only need like 7, and then I wouldn't have to add or remove anymore, but would have to rework a big chunk of code for that, and consider it a last resort. The game already runs smooth, it just has a few barely noticeable stutters here and there that I would like to avoid.
private void addPad() {
lilypads.add(new LilyPad(placement[MathUtils.random(0, 2)], 300, false));
lilypads.get(lilypads.size() - 1).init();
}
public void update(float dt) {
speed = speedManager.speed(dt);
speedManager.update(dt);
// update grass
if(!grass.shouldStop()) {
grass.update(dt, speed);
frog.introPlay(speed);
}
// update lily pads
for(int i = 0; i < lilypads.size(); i++) {
lilypads.get(i).update(dt, speed);
This is where I call the addPad() method. It basically says if the last added pad is fully visible on screen, add the next.
if(lilypads.get(i).getY() < (Game.HEIGHT - Game.SIZE) && lilypads.get(i).past()) {
addPad();
}
// hop frog
if(lilypads.get(i).getY() < hopY && lilypads.get(i).past2()) {
// play hop
if(lilypads.get(i).getX() > frog.getX() - Game.SIZE / 2 && lilypads.get(i).getX() < frog.getX() + Game.SIZE / 2){
frog.incrementScore();
Jukebox.play("hop");
lilypads.get(i).splash();
frog.play(speed);
} else {
Jukebox.stopAll();
Save.gd.setTempScore(frog.getScore());
gsm.setState(GameStateManager.GAMEOVER);
return;
}
return;
}
if(lilypads.get(i).getY() < (-Game.SIZE)){
lilypads.remove(i);
}
}
}
public void draw() {
// draw grass
if(!grass.shouldStop()) {
grass.draw(sb);
}
// draw pads
for(int i = 0; i < lilypads.size(); i++){
lilypads.get(i).draw(sb, true);
}
// draw frog
frog.draw(sb, speed, true);
sb.setColor(1, 1, 1, 1);
sb.begin();
font.draw(sb, Long.toString(frog.getScore()),
Game.WIDTH * 0.92f -font.getBounds(Long.toString(frog.getScore())).width, Game.HEIGHT * 0.958f);
sb.end();
}
public void handleInput() {
}
public void dispose() {
sb.dispose();
Jukebox.stopAll();
}
Thanks for the support.
Use libGDX's custom collections for that. You can find all of them here.
In your case you want to use an Array. They are optimized to not cause the garbage collector to kick in, for example by re-using the iterators.
With a standard ArrayList the following piece of code in your render method would create a new Iterator in every frame.
for(LilyPad lilyPad : lilipads){
lilypad.draw(sb, true);
}
In general try to avoid using new whenever you can. This wiki article might help you with it and it explains how pooling works.
Another common trick to avoid GC especially when working with Vector2 or Vector3 is keeping a private final Vector2 tmp = new Vector2() and always only using tmp.set(x, y) to change it. This principle could also be applied to any other custom class that you are creating yourself to hold some data.
I'm experimenting with some Piccolo to create a zoomable interface.
I'm creating a rectangle on a canvas with some PText on it. Now when zooming, I want to change the text to something different.
I've done this in my initialize:
//
//specify the current Piccolo PCanvas
//
m_canvas = getCanvas();
m_canvas.removeInputEventListener(m_canvas.getPanEventHandler());
//m_canvas.addInputEventListener(new ClickAndDragHandler(m_canvas));
//
//add nodes to the collection -> adding to the collection = adding to the canvas
//
m_nodecollection = new NodeCollection(m_canvas);
RectangleNode node_links = new RectangleNode();
node_links.setBounds(10, 10, 500, 500);
m_nodecollection.addNode(node_links);
RectangleNode node_rechts = new RectangleNode();
node_rechts.setBounds(600,10,500,500);
m_nodecollection.addNode(node_rechts);
PImage node_arrowLeft = new PImage("left.gif");
node_arrowLeft.setBounds(600, 550, node_arrowLeft.getWidth(), node_arrowLeft.getHeight());
m_nodecollection.addNode(node_arrowLeft);
PImage node_arrowRight = new PImage("right.gif");
node_arrowRight.setBounds(680, 550, node_arrowRight.getWidth(), node_arrowRight.getHeight());
m_nodecollection.addNode(node_arrowRight);
m_nodecollection.connectNodesWithLine(node_rechts, node_arrowRight, true);
m_nodecollection.connectNodesWithLine(node_rechts, node_arrowLeft, true);
PText node_text = new PText("Zoomlevel Not Supported");
node_text.setBounds(180,150, node_text.getWidth(), node_text.getHeight());
m_nodecollection.connectNodesWithLine(node_links, node_text, true);
m_nodecollection.addNode(node_text);
node_links.addChild(node_text);
node_links.setCollection(m_nodecollection);
Created my own rectangle class with the whole nodecollection and PText as membervar.
public class RectangleNode extends PNode{
private Rectangle2D m_rectangle;
private NodeCollection collection;
private PText text;
public RectangleNode()
{
m_rectangle = new Rectangle2D.Double();
}
public Rectangle2D getRectangle()
{
if(m_rectangle == null)
m_rectangle = new Rectangle2D.Double();
return m_rectangle;
}
public boolean setBounds(double x, double y, double w, double h)
{
if(super.setBounds(x, y, w, h))
{
m_rectangle.setFrame(x, y, w, h);
return true;
}
return false;
}
public void setCollection(NodeCollection collection)
{
this.collection = collection;
}
public void setText(PText text)
{
this.text = text;
}
public void paint(PPaintContext paintcontext)
{
Graphics2D g2 = paintcontext.getGraphics();
//niet ingezoomd
if(paintcontext.getScale() <= 0.2)
g2.setPaint(Color.BLACK);
//half ingezoomd
if(paintcontext.getScale() > 0.2 && paintcontext.getScale() < 0.7)
{
g2.setPaint(Color.BLACK);
}
//volledig ingezoomd
if(paintcontext.getScale() > 0.7)
{
g2.setPaint(Color.RED);
g2.fill(getRectangle());
g2.setPaint(Color.BLACK);
g2.draw(getRectangle());
}
}
}
Now, I thought I could change the text like this: text.setText("Tester"); but It doesn't work, also when for example settext and then add the node to collection then it displays over the current text with a huge error...
Can someone help me please?
kind regards,
Consider posting the whole example as SSCCE, looks like some parts of the code are missing. It is not clear how you actually execute setText.
It may be easier to compose existing nodes and listen to events fired from camera. Consider the following example that draws a rectangle with some text which gets updated according to a zoom level:
import java.awt.Color;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import edu.umd.cs.piccolo.PCamera;
import edu.umd.cs.piccolo.nodes.PPath;
import edu.umd.cs.piccolo.nodes.PText;
import edu.umd.cs.piccolo.util.PPaintContext;
import edu.umd.cs.piccolox.PFrame;
public class TestRectZoom extends PFrame {
public TestRectZoom() {
super("TestRectZoom", false, null);
}
public void initialize() {
getCanvas().setInteractingRenderQuality(
PPaintContext.HIGH_QUALITY_RENDERING);
getCanvas().setDefaultRenderQuality(
PPaintContext.HIGH_QUALITY_RENDERING);
getCanvas().setAnimatingRenderQuality(
PPaintContext.HIGH_QUALITY_RENDERING);
final PPath rect = PPath.createRectangle(100, 100, 200, 200);
rect.setPaint(Color.GREEN);
getCanvas().getLayer().addChild(rect);
final PText text = new PText(getZoomLevelString());
rect.addChild(text);
text.centerFullBoundsOnPoint(rect.getBounds().getCenterX(), rect
.getBounds().getCenterY());
getCanvas().getCamera().addPropertyChangeListener(
PCamera.PROPERTY_VIEW_TRANSFORM, new PropertyChangeListener() {
public void propertyChange(final PropertyChangeEvent evt) {
text.setText(getZoomLevelString());
if (getCanvas().getCamera().getViewScale() > 0.9) {
rect.setPaint(Color.GREEN);
} else {
rect.setPaint(Color.RED);
}
}
});
}
private String getZoomLevelString() {
return "Zoom level:"
+ String.format("%.2f", getCanvas().getCamera().getViewScale());
}
public static void main(final String[] args) {
new TestRectZoom();
}
}
That's how the result looks like:
You are looking for semantic zooming available on the Piccolo Patterns page.
Pattern 17: Semantic Zooming
http://www.piccolo2d.org/learn/patterns.html
I solved the problem like Aqua suggested.
I've just starting delving into the wonders of Java ME but have become frustrated when trying to create a thread...
Below is the code which compiles absolutely fine. However, as soon as I install it on my G600 and run it, 'Java Game Error' pops up.
My method of putting it in a jar file and installing it works, as I have created a game with no threads and that works fine.
import java.util.Random;
import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;
import javax.microedition.midlet.*;
public class CanvasTest extends MIDlet {
Display display;
public CanvasTest() {
}
public void startApp() {
TestCanvas thecanvas = new TestCanvas();
display = Display.getDisplay(this);
display.setCurrent(thecanvas);
}
public void pauseApp() {}
public void destroyApp(boolean unconditional) {}
}
class TestCanvas extends GameCanvas implements Runnable {
Font font;
int width;
int height;
boolean running = true;
public TestCanvas() {
super(false);
setFullScreenMode(true);
width = getWidth();
height = getHeight();
Thread thisThread = new Thread(this);
thisThread.start();
}
public void paint(Graphics g) {
Random rand = new Random();
g.setColor(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255));
g.fillRect(0, 0, width, height);
}
public void run() {
while(running) {
paint(getGraphics());
flushGraphics();
try {
Thread.sleep(50);
}
catch(InterruptedException ex) {}
}
}
};
Note: yes, this is not the game, it merely demonstrates the problem I am facing.
Thanks in advance!
Just a wild guess, but a general rule in Java is that you can't "touch" the UI out of the main thread. Well, this a little bit roughly explained, but there are many articles about the topic.
I suggest you to avoid calling UI methods like paint() or flushGraphics() from a separate Thread.
I hope it helps.
did you test it at emulator prior to phone? if not - why? if yes - how did it go?
regarding the code it looks OK to me except for the slippery two lines where you create and start thread from constructor. I'd rather move these two lines at the end of startApp
public void startApp() {
TestCanvas theCanvas= new TestCanvas();
display = Display.getDisplay(this);
display.setCurrent(theCanvas);
new Thread(theCanvas).start(); // add here and...
}
//...
public TestCanvas() {
super(false);
setFullScreenMode(true);
width = getWidth();
height = getHeight();
// ...and remove here
// Thread thisThread = new Thread(this);
// thisThread.start();
}