I'm trying to create an external Archer class for a tower defense game with its own draw function, but I'm having trouble accessing the image function from within the Archer class. I've got a workaround working as shown below but I feel like there must be a better way to do it.
public class Sketch extends PApplet {
static PImage archerImg;
static Sketch s;
public void setup() {
s = this;
archerImg = loadImage("img/Archerv2.png");
}
}
class Archer {
public Archer(int x, int y) {
this.x = x;
this.y = y;
image = Sketch.archerImg;
}
public void draw() {
Sketch.s.image(image, x, y);
}
}
Can anyone recommend a more elegant way to do this?
As far as I understand you're trying to get a reference to the sketch so you can simply use image() when rendering an instance of Archer from the scope of the class itself.
One option could to take advantage of the fact that g is PApplet's global PGraphics buffer which provides the rendering functions (such image()):
public class Sketch extends PApplet {
PImage archerImg;
Archer archer;
public void setup() {
size(300, 300);
archerImg = loadImage("img/Archerv2.png");
archer = new Archer(150, 150, archerImg);
}
public void draw(){
background(0);
archer.draw(g);
}
public void mouseDragged(){
archer.x = mouseX;
archer.y = mouseY;
}
}
class Archer {
int x, y;
PImage image;
public Archer(int x, int y, PImage image) {
this.x = x;
this.y = y;
this.image = image;
}
public void draw(PGraphics g) {
g.image(image, x, y);
}
}
The advantage is that if you chose to have multiple buffers(e.g. a background layer, sprites layer, a HUD layer, etc.) you can explicitly tell the Archer instance into which layer it should render to.
Alternatively you can pass the sketch directly in the constructor which you can reuse later to call image:
public class Sketch extends PApplet {
PImage archerImg;
Archer archer;
public void setup() {
size(300, 300);
archerImg = loadImage("img/Archerv2.png");
archer = new Archer(this, 150, 150, archerImg);
}
public void draw(){
background(0);
archer.draw();
}
public void mouseDragged(){
archer.x = mouseX;
archer.y = mouseY;
}
}
class Archer {
int x, y;
PImage image;
PApplet parent;
public Archer(PApplet parent, int x, int y, PImage image) {
this.x = x;
this.y = y;
this.image = image;
this.parent = parent;
}
public void draw() {
parent.image(image, x, y);
}
}
If you can to pass the Sketch directly you can as well, though this would mean even tighter coupling:
public class Sketch extends PApplet {
public PImage archerImg;
Archer archer;
public void setup() {
size(300, 300);
archerImg = loadImage("img/Archerv2.png");
archer = new Archer(this, 150, 150);
}
public void draw(){
background(0);
archer.draw();
}
public void mouseDragged(){
archer.x = mouseX;
archer.y = mouseY;
}
}
class Archer {
int x, y;
Sketch parent;
public Archer(Sketch parent, int x, int y) {
this.x = x;
this.y = y;
this.parent = parent;
}
public void draw() {
parent.image(parent.archerImg, x, y);
}
}
Without more context on the design of your game it's tricky to get an exact answer, however you have multiple options above. Personally, even though it's slightly more verbose I'd go with the PGraphics option since less coupled than the other options.
One option would be to use preload() to load the image; it is run once before the rest of your code. The following will run in Processing IDE using p5.js mode. The img folder with the archer image inside needs to be in your sketch folder.
let archerImg;
function preload(){
archerImg = loadImage('img/Archerv2.png');
}
function setup() {
createCanvas(500,500);
background(209);
archer = new Archer(50,60);
}
function draw() {
archer.display();
}
class Archer {
constructor(x, y) {
this.x = x;
this.y = y;
}
display() {
image(archerImg, this.x, this.y );
}
}
Related
I'm a beginner in libGdx and I'm making a simple game.
I wrote a nested class. As you can see here:
public class classes{
public class GameObject{
//declaring section
private Texture texture;
private Texture[] spawnAnimation;
private Texture[] deathAnimation;
private Texture[] damageAnimation;
private Texture[] moveAnimation;
private Texture[] fightAnimation;
private Texture[] talkAnimation;
private SpriteBatch batch = new SpriteBatch();
private float width, height;
private float x, y;
private Sound spawnSound, touchSound, deathSound, objSound, moveSound, fightSound;
public GameObject(Texture txtr, float width, float height, float x, float y) {
this.texture = txtr;
this.width = width;
this.height = height;
this.x = x;
this.y = y;
}
//this method checks wether our game object is over an other object
public boolean isOver(GameObject obj){
if(((this.x >= obj.x) && (this.x <= (obj.x + obj.width))) && ((this.y >= obj.y) && (this.y <= (obj.y + obj.height))))
return true;
return false;
}
//this method sets the object bounds
public void setBounds(float width, float height, float x, float y){
this.width = width;
this.height = height;
this.x = x;
this.y = y;
}
//this method draws the object
public void draw(){
batch.draw(this.texture, this.width, this.height, this.x, this.y);
}
//animation setting section
public void setSpawnAnimation(Texture[] texture){
this.spawnAnimation = texture;
}
public void setDeathAnimation(Texture[] texture){
this.deathAnimation = texture;
}
public void setDamageAnimation(Texture[] texture){
this.damageAnimation = texture;
}
public void setMoveAnimation(Texture[] texture){
this.moveAnimation = texture;
}
public void setFightAnimation(Texture[] texture){
this.fightAnimation = texture;
}
public void setTalkAnimation(Texture[] texture){
this.talkAnimation = texture;
}
//sounds setting section
public void setSpawnSound(Sound sound){
this.spawnSound = sound;
}
public void setTouchSound(Sound sound){
this.touchSound = sound;
}
public void setDeathSound(Sound sound){
this.deathSound = sound;
}
public void setObjSound(Sound sound){
this.objSound = sound;
}
public void setMoveSound(Sound sound){
this.moveSound = sound;
}
public void setFightSound(Sound sound){
this.fightSound = sound;
}
//animation and behavior section
public void spawn(){
batch.begin();
for(int i=0;i<=this.spawnAnimation.length;i++){
this.texture = this.spawnAnimation[i];
this.draw();
try
{
Thread.sleep(0, 1);
}
catch (InterruptedException e)
{}
}
batch.end();
}
public void die(Texture[] deathTexture){
}
}
}
I went to the other file, in the other class, and I tried to set the object texture
public class MyGdxGame implements ApplicationListener{
Texture texture;
SpriteBatch batch;
classes.GameObject gun;
#Override
public void create()
{
batch = new SpriteBatch();
gun = new classes.GameObject(new Texture(Gdx.files.internal("gun.png")), 0, 0, 300, 300);
}
#Override
public void render()
{
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.end();
}
#Override
public void dispose()
{
}
#Override
public void resize(int width, int height)
{
}
#Override
public void pause()
{
}
#Override
public void resume()
{
}
}
once I try to access it. I face this error: aan instance for an enclosing class is required
note: I'm making this game for android. using AIDE ide which offers developing apps and games directly on your phone.
Inner classes that are declared without the keyword static are tethered to the outer class they exist in.
To fix
public static class GameObject{
Why would you want an inner class without static? Here's an example
public class Game {
private Data gameData;
class MyListener {
public void receiveNewData(Data newData) {
//update gameData with the new data
}
}
}
If MyListener was static, it would not be able to access Game's gameData
I am trying to add a custom circle to a JPanel, see this:
graphicPanel = new GraphicPanel();
JTextArea text = new JTextArea("1233", 5, 10);
graphicPanel.add(text);
Circle circle = new Circle();
circle.setX(30);
circle.setY(30);
circle.setDiameter(30);
graphicPanel.add(circle);
graphicPanel.repaint();
graphicPanel.revalidate();
GraphicPanel is just a custom JPanel that doesn't do anything interesting yet (just holds a list that is not used yet)
GraphicPanel.java
public class GraphicPanel extends JPanel {
private static final long serialVersionUID = -3813468764873993369L;
private List<Node> nodes = new ArrayList<Node>();
public GraphicPanel() {
}
public void addNode(Node node) {
nodes.add(node);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.CYAN);
g.fillOval((30 - 30 / 2), (30 - 30 / 2), 30, 30);
}
}
Circle.java
public class Circle extends JComponent {
private static final long serialVersionUID = 628299863960706428L;
private int x;
private int y;
private int diameter;
private Color color;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getDiameter() {
return diameter;
}
public void setDiameter(int diameter) {
this.diameter = diameter;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(color);
g.fillOval((x - diameter / 2), (y - diameter / 2), diameter, diameter);
}
}
The JTextArea appears, the circle does not. If I add the draw code for the circle directly to the paintComponent() of graphicPanel, then a circle appears:
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.CYAN);
g.fillOval((30 - 30 / 2), (30 - 30 / 2), 30, 30);
}
So the draw code itself should be fine (I considered that maybe the circle is drawn somewhere where I cant see it but this is not the case).
What do I have to change to make the circle appear? I want to draw it like this and not with g.fillOval() in paintComponent() of GraphicPanel.
I am using Java 8
The first thing is that a JPanel has a FlowLayout as a default layout manager.
A FlowLayout honors the preferred size of a Component, but Circle doesn't have a specific one, so its size is (0,0).
You may want to override getPreferredSize to give it one, or use a layout manager that will still give a size to your Circle (e.g a BorderLayout where you add your component to CENTER) .
For later , you may also want to override getMaximumSize and getMinimumSize.
The second thing is that getX and getY are existing methods from JComponent, that your code overrides (probably not on purpose).
Those methods tell the position of this component within its container and would mess up the layout if you play with them (here your Circle is located at 30,30 inside GraphicPanel and gets hidden by the textarea).
getX()
the current x coordinate of the component's origin
In the following example, I changed the name and the accessor methods of x and y to avoid overriding getX and getY (there was actually no need to change the names of x and y variables, it is just to keep coherent with those accessor methods names).
A "preferred size" has also been set by adding an overriden getPreferredSize method, computing its optimal size.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JComponent;
public class Circle extends JComponent {
private static final long serialVersionUID = 628299863960706428L;
private int xCoo;
private int yCoo;
private int diameter;
private Color color;
#Override
public Dimension getPreferredSize() {
return new Dimension(diameter + xCoo, diameter + yCoo);
}
/* #Override
public Dimension getMinimumSize() {
return new Dimension(diameter + xCoo, diameter + yCoo);
}
#Override
public Dimension getMaximumSize() {
return new Dimension(diameter + xCoo, diameter + yCoo);
}*/
public int getXCoo() {
return xCoo;
}
public void setXCoo(final int xCoo) {
this.xCoo = xCoo;
}
public int getYCoo() {
return yCoo;
}
public void setYCoo(final int yCoo) {
this.yCoo = yCoo;
}
public int getDiameter() {
return diameter;
}
public void setDiameter(final int diameter) {
this.diameter = diameter;
}
public Color getColor() {
return color;
}
public void setColor(final Color color) {
this.color = color;
}
public void paintComponent(final Graphics g) {
super.paintComponent(g);
g.setColor(color);
g.fillOval((xCoo - diameter / 2), (yCoo - diameter / 2), diameter, diameter);
}
}
Also note that JComponent has set/getBackground and set/getForeground methods that you may find useful to set and get background and foreground Color.
I am working on a game.I want to add images to the applet but they do not appear there until I resize or minimize the applet.I do not know whether my image observer is wrong or not.
The images are in right path.Their in build and class folder with correct spelling. What is wrong with my code?
any help would be much appreciated.
public class game extends JApplet implements KeyListener, Runnable {
ScreenDir sd;
Image ball;
Image flower;
public void init() {
sd = new ScreenDir(this);
ball = getImage(getCodeBase(), "ball.jpg");
ballb = new Ball(50, 50, 30, 40, Color.red, ball, sd);
sd.add(ball);
flower = getImage(getCodeBase(), "flower.gif");
//class Flower is just defined like class Ball
flowerf = new Flower(60, 100, 30, 30, Color.green, flower, sd);
sd.add(flower);
}
public void paint(Graphics g) {
sd.draw(g);
}
//These are for moving the ball
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void run() {
while (true) {
repaint();
try {
Thread.sleep(15);
} catch (InterruptedException ex) {}}}
public class ScreenDir {
ArrayList<Object> olist;
JApplet parent;
public ScreenDir(JApplet parent) {
this.parent = parent;
olist = new ArrayList<>();
}
public void add(Object o) {
olist.add(o);
}
Image Img;
Graphics oG;
Img = parent.createImage(parent.getWidth(), parent.getHeight());
oG = offImg.getGraphics();
for(int i = 0;i<olist.size ();i++){
olist.get(i).draw(offG);
}
g.drawImage (Img,0, 0, parent);
}
public class Ball extends Object {
ScreenDir sd;
public ball(int x, int y, int w, int h, Color c, Image pi, ScreenDir sd) {
{
this.sd = sd;
}
public void draw(Graphics g) {
g.drawImage(pi, x, y, w, h, null);
}
public abstract class Object {
int x, y, w, h;
Color c;
Image pi;
public Object(int x, int y, int w, int h, Color c, Image pi) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.c = c;
this.pi = pi;
}
public abstract void draw(Graphics g) {
}
Applets load images asynchronously, so it is likely the images are not fully loaded before the applet goes to paint. But every Java component worth mentioning implements an ImageObserver that means it will get updates on image loading. So to fix the problem, change this:
g.drawImage(pi, x, y, w, h, null);
To this:
g.drawImage(pi, x, y, w, h, this);
Update
I mistakenly thought the drawImage method was part of the JApplet (which is an ImageObserver). You might simply change the method declaration & painting line to:
public void draw(Graphics g, ImageObserver io) {
g.drawImage(pi, x, y, w, h, io);
}
Then to call it, change:
sd.draw(g);
To:
sd.draw(g, this);
I've run into a design problem in my java code. My application uses missiles, and there are different types of missiles that all work identical except they have 3 unique attributes. The constructor of a missile must know these attributes. I decided to make missile an abstract class, but I can't assign values to protected variables in a subclass outside of a method/constructor. Also I can't declare the variables in the constructor, because I must make the call to the super-constructor first thing.
How can I be smart about this problem?
public abstract class Missile {
private int x, y;
private Image image;
boolean visible;
private final int BOARD_WIDTH = 390;
protected final int MISSILE_SPEED;
protected final int MISSILE_HEIGHT;
protected String file;
public Missile(int x, int y) {
ImageIcon ii =
new ImageIcon(this.getClass().getResource(file));
image = ii.getImage();
visible = true;
this.x = x;
this.y = y - Math.floor(MISSILE_HEIGHT/2);
}
public Image getImage() {
return image;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public boolean isVisible() {
return visible;
}
public void move() {
x += MISSILE_SPEED;
if (x > BOARD_WIDTH)
visible = false;
}
}
And there is an ideal implementation of a subclass, except it doesn't work. (it can't recognize the protected variables). What do I do?
public class Laser extends Missile {
MISSILE_SPEED = 2;
MISSILE_HEIGHT = 5;
file = "laser.jpg";
public Laser(int x, int y) {
super(x, y);
}
}
I think the best way to do what you want it to do is make abstract methods in Missile that the subclasses have to implement. For example, add these to Missile:
public abstract int getMissileSpeed();
public abstract int getMissileHeight();
public abstract int getFileName();
Then your subclass has to implement it, and you can make it constant like so:
public class Laser extends Missile {
public Laser(int x, int y) {
super(x, y);
}
public int getMissileSpeed() {
return 2;
}
public int getMissileHeight() {
return 5;
}
public String getFileName() {
return "laser.jpg";
}
}
edit: And then of course anywhere that you want to retrieve the constant value you just call those methods.
Change the base class fields and constructors to
protected final int speed;
protected final int height;
public Missile(int x, int y, int speed, int height, String file) {
ImageIcon ii =
new ImageIcon(this.getClass().getResource(file));
image = ii.getImage();
visible = true;
this.speed = speed;
this.height = height;
this.x = x;
this.y = y - Math.floor(height/2);
}
And the subclass to:
public class Laser extends Missile {
public Laser(int x, int y) {
super(x, y, 2, 5, "laser.jpg");
}
...
}
The attributes are already in the base class, so they must not be redefined in the subclass. All-uppercase naming is reserved to constants in Java.
I'm not sure if missile needs to be an abstract class, but I think something like this might be what you're going for:
public abstract class Missile {
private int x, y;
private Image image;
boolean visible;
private final int BOARD_WIDTH = 390;
protected final int MISSILE_SPEED;
protected final int MISSILE_HEIGHT;
public Missile(int x, int y, int speed, int height, String file) {
MISSILE_SPEED = speed;
MISSILE_HEIGHT = height;
ImageIcon ii = new ImageIcon(this.getClass().getResource(file));
image = ii.getImage();
visible = true;
this.x = x;
this.y = y - Math.floor(MISSILE_HEIGHT/2);
}
}
public class Laser extends Missile {
public Laser(int x, int y) {
super(x, y, 2, 5, "laser.jpg");
}
}
Create an interface and put all your final fields in it.
Now implement this interface within Missile and Laser both. At least that would solve the issue of access.
So what I want to achieve, is to create a simple program that lets the user create rectangles on the screen and then move them around.
I know that I can declare a new object in the code (i.e rectangle i = new rectangle(x,y,sizex,sizey)) however that will create only one, moreover it forces me to declare it in the code:
block1 = new block
block2 = new block
etc
Question is: How can I let the user create infinite rectangles with the use of lets say a button (not necessarily a button, it can be anything) and then let the user be capable of modyfing them (location/size etc).
Examples would be nice. I just feel there is a better way than declaring a gazillion objects in the code and then displaying them one by one. In C++ i could declare a malloc expandable container that would just hold the values, and then just display things using these values, not sure for java.
Simple code for reference:
public static void main(String[] args){
JFrame frame = new JFrame("A.L.T.E.S");
//Container container = frame.getContentPane();
frame.setSize(1024, 768);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Block object = new Block(10,10,20,20);
frame.add(object);
object.reDraw();
}
public class Block extends JPanel{
int yPos;
int xPos;
int xSize;
int ySize;
public Block(int xPos, int yPos, int xSize, int ySize){
this.xPos = xPos;
this.yPos = yPos;
this.xSize = xSize;
this.ySize = ySize;
}
public void setYPos(int yPos){
this.yPos = yPos;
}
public void setXPos(int xPos){
this.xPos = xPos;
}
public void setXSize(int xSize){
this.xSize = xSize;
}
public void setYSize(int ySize){
this.ySize = ySize;
}
public int getYPos(){
return yPos;
}
public int getXPos(){
return xPos;
}
public int getYSize(){
return ySize;
}
public int getXSize(){
return xSize;
}
public void reDraw(){
repaint();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(xPos, yPos, xSize, ySize);
}
}
Nothing in Java is simple.
First, Java already has a Rectangle class that holds the origin and size of a Rectangle. In your code, you're making all the rectangles blue. Suppose you want the user to set the color of a Rectangle. You can define your Block class like this.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
public class Block {
private Color color;
private Rectangle rectangle;
public Block(int x, int y, int width, int height) {
this(new Rectangle(x, y, width, height));
}
public Block(Rectangle rectangle) {
this.rectangle = rectangle;
this.color = Color.BLUE;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public Rectangle getRectangle() {
return rectangle;
}
public void draw(Graphics g) {
g.setColor(getColor());
g.fillRect(rectangle.x, rectangle.y,
rectangle.width, rectangle.height);
}
}
Next, you need a model class to hold all of the Blocks that your user defines.
Something like this BlockList class.
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;
public class BlockList {
private List<Block> blockList;
public BlockList() {
this.blockList = new ArrayList<Block>();
}
public void init() {
this.blockList.clear();
}
public void addBlock(Block block) {
this.blockList.add(block);
}
public void draw(Graphics g) {
for (int i = 0; i < blockList.size(); i++) {
blockList.get(i).draw(g);
}
}
}
Now that you have your GUI model defined, you would build your GUI. Your drawing JPanel would call the draw method in your BlockList class.
I strongly suggest that you go through the Oracle tutorial on Swing. Go through the complete tutorial before you attempt to create a Swing GUI.
In java, use a Collection. In this case, use a List. You can create an ArrayList<Block>, then invoke add to add new rectangles. Then, you can iterate through this collection by using iterator, then calling hasNext and next on the iterator to find all the elements. I'm sure this brief hypnosis is going to be confusing, and if so, check out the Java Tutorial on Collections, which explains everything in all the detail you are going to need.