G'day,
I've been trying to finish an assignment and am learning a lot about OO and Java. Nearing the end of this project so very happy but thought I'd try get an interesting question out there, at least for me because of my lack in understanding.
Some background to help clarify, I've used 2D ArrayLists to model a map. I've made a "copy" of the original so that I can update movements and locations, some are permitted some are not.
I use a method to determine which movement is okay and to then update the "copy". There are two classes involved here.
Class GameEngine {
void runGameLoop(ArrayList<ArrayList<String>> map) {
World w = new World();
w.setOriginalMap(map);
while(1) {
w.checkMovement;
}
}
Class World {
ArrayList<ArrayList<String>> originalMap;
void setOriginalMap(ArrayList<ArrayList<String>> map) {
originalMap = new ArrayList<>(map);
}
void checkMovement (String keyEvent, Player obj1) {
ArrayList<ArrayList<String>> copyMap = originalMap;
obj1.setPlayer();
printMap(copyMap, obj1);
}
The issue is that the movement is updated on the map, but the player is now in multiple locations being the ones previous... my map has turned into more of a route. Does this has something with using the same object reference? I make a "copy" inside of the Class method so isn't this local?
Would appreciate some insight.
Related
I am programming a 2D game in Java Swing. I have created several LinkedLists to hold instances of classes Tower, Entitiy and TowerBuildButtons. After I did this I realized that I want to have a superclass to all of these: Selectable. This is because all of these elements should have the capability to be selected and hovered over with the mouse. So I created the superclass Selectable and an additional LinkedList selectables.
The problem I am facing here is: When I add additional objects to the smaller lists (entities, towers, etc...) I also want them to be added to the larger selectables list. I can think of one solution to this. Creating a new add-method and making sure that when new objects are added, they are also added to selectables list. Example:
void addTower(Tower t) {
towers.add(t); //Adding new tower object to the list of towers
selectables.add(t); //Also adding the object to the list of selectables
}
However, I suspect there is a better way of solving this problem. So: How can I make sure that the selectables list is updated when its sublists are? or: How can I make a list of sublists that updates properly when new elements are added to the sublists?
Code for my linked lists:
//LISTS OF GAME OBJECTS
public static LinkedList<Entity> entities = new LinkedList<Entity>();
public static LinkedList<Block> blocks = new LinkedList<Block>();
public static LinkedList<Tower> towers = new LinkedList<Tower>();
public static LinkedList<Projectile> projectiles = new LinkedList<Projectile>();
//List of anything that is a subclass of Selectable(buildBtns, towers, entities)
public static LinkedList<Selectable> selectables = new LinkedList<Selectable>();
//LISTS OF INTERFACE OBJECTS
public static LinkedList<BuildTowerButton> buildBtns = new LinkedList<BuildTowerButton>();
Higher memory imprint
I would suggest you create a Board singleton.
Then adding a Tower, for instance, would be handled by a Board.add(Tower).
This way, you could implement the Board in such a way that adding a Tower registers it both in the towers and selectables collection:
public Board add(Tower tower){
towers.add(tower);
selectables.add(tower);
return this;
}
Lazy evaluation
Another idea which would reduce memory imprint but improve CPU usage would be to simply compute your selectables on demand:
public List<Selectable> getSelectables(){
return new LinkedList<Selectable>().addAll(/*first list of Selectables*/)
.addAll(/*second list of Selectables...*/);
}
Note
I would advocate against usage of public static variables and go for the singleton, so that you are sure that there is exactly one way of adding a Tower to your Board, hence no one can "forget" to update the selectables collection as well.
short description of what I am trying to do: I'm building a simple game where the user controls a vehicle and after some time more and more ghosts begin following the player, they follow the same trajectory as the player did with a delay.
To accomplish this I create an Array that contains the history of the player's location as a sequence of points. The problem, however, is that when I look at the data stored in this array I see that on all indices only the most recent location is stored.
First I create the array in a botManager class:
public class BotManager {
private ArrayList<Bots> bots;
private List<Point> history;
BotManager() {
history = new ArrayList<>();
bots = new ArrayList<>();
}
Then in the update method of the manager class I add the current location of the player to the array
public void update(Point currLoc) {
history.add(currLoc);
for (Bots bot : bots) {
bot.setLocationData(history);
bot.update();
}
}
A look at the update method in the main GameView class, in case I forgot something here
public void update() {
player.update(playerPoint);
botManager.update(playerPoint);
}
In the bots class' constructor I pass the history list (locationData) and determine its length to find out the delay in positioning. After which the following code handles the position of the bot.
#Override
public void update() {
loc = locationData.get(delay - 1);
this.rectangle = new Rect(loc.x - Constants.BOTSIZE/2, loc.y - Constants.BOTSIZE/2,
loc.x + Constants.BOTSIZE/2, loc.y + Constants.BOTSIZE/2);
}
To get back to the problem, whenever I check the contents of the history array, I find that it only contains one point on all indices, and its the most recent even when I moved the player, causing the ghost to always remain on top of me.
So my question here is, what am I doing wrong here?
Not clear from your posted code, but could it be, that you are just modifying the Point which you are adding instead renewing your object's Point objects?
Try
public void update(Point currLoc) {
history.add(new Point(currLoc)); // new Point object added here
for (Bots bot : bots) {
bot.setLocationData(history);
bot.update();
}
}
So this is my very first mini-javaproject and I have been stuck for days now on the basic structure and the (non existing) relation between anything within my code. I linked the code in my comment below, could not paste it in here for some reason - (Main is empty, so did not copy it.)
So I spent some time getting my head around the basics of Java (as my first programming adventure) and to be honest I felt pretty confident. (On Codewars I completed like 100+ Katas, but of course those are "single-class", so I was not prepared for the "real world.)
It is hard to exactly pinpoint my question, but I will try to give some examples.
1, (Main is empty right now, but anyway) Basically "nothing can be used" in main. Like methods of objects, like room1, or player1, etc.
2, In my Room.java line 21-22 why is the object room1 not visible? Why does Intellij say "Unknown class: RoomArray if I just created that very thing before??
3, I understand that I am supposed to have my variables set to private, which I plan to do later on. Also, I should use setter and getter methods, which I tried to do so with basically everything. But for example in Player.java I have this
Player player1 = new Player(300, 50, "Conan", 75, false);
public Player getPlayer1() {
return player1;
}
and if I try to use the getPlayer1() method in any other class it just simply can not see/access it?
3, And to make me even more confused Room1 class has access to getMyDungeon () method created in the Dungeon class. Why is that so?
(Maybe it has to do with inheritance? The fact that Room1 extends Room which extends Room? But if so, it seems strange because not all classes can have a HAS-A or IS-A relationship with something. An example - if I create all 10 Rooms later on as Room1, Room2, etc. in separate classes, how could I ever create a Room [] array containing them? No matter where I started to do that it will always give me the error "Cannot resolve smybol" for all the Room objects...)
I have spent the past few days reading up on the topic and understand it all, but still when I try to build this project it all falls apart. I realize that an experienced programmer might not even my question because how basic it is, but if anyone can help me to get this whole thing clear in my head, I would appreciate it. (Really not looking for the complete code, but just some direction I should go, or the missing step, etc.)
It seems you to be trying to create an object within the class of that object The correct use is:
public static void main(String[] args){
Player player1 = new Player(300, 50, "Conan", 75, false);
}
or if you want your Room class to have a lot of players
public class Room {
//this object will be create when you do Room room = new Room();
List<Player> players = new ArrayList<>();
public void createPlayer(){
players.add(new Player());
}
//this is a getter
public List<Player> getPlayers() {
return players;
}
}
your Player:
public class Player {
//Fields and Methods
}
and your main:
public static void main(String[] args)
{
Room room =new Room();
room.createPlayer();
for (Player p:room.getPlayers()) {
//p.doSomething
}
}
if you want an object to be created without the need to create an instance from the outside you need to use the static keyword (don't do that unless you know what you are doing)
static Player player1 = new Player(300, 50, "Conan", 75, false);
public static Player getPlayer1() {
return player1;
}
Say I have the following code snippet to create colored vegetables for a small random game I'm making to practice separating object properties out of object classes:
List<Vegetable> vegList = new ArrayList<Vegetable>();
Map<MyProperty, Object> propertyList = new HashMap<MyProperty, Object>();
propertyList.put(MyProperty.COLOR, "#0000BB");
propertyList.put(MyProperty.TYPE, MyType.VEGETABLE);
propertyList.put(MyProperty.COMMONNAME, "Potato");
vegList.add(new Vegetable("Maisie", propertyList));
propertyList.put(MyProperty.COLOR, "#00FF00");
propertyList.put(MyProperty.COMMONNAME, "Poisonous Potato");
vegList.add(new Vegetable("Horror", propertyList));
I realized while doing this (making my own example from Head First OOA&D, basically) I have no idea why changing propertyList the second time doesn't affect the values previously set within Maisie.
I followed the structure provided by the book, but the first time around I was creating a new HashMap for each individual Vegetable object, before adding it to the list. The book shows that's unnecessary but doesn't go into why.
All I can see is the interpreter is making a choice to create a new instance of the hashmap when it's specified in the Vegetable constructor the second time around. But why?
How does it know that I'd rather have a different HashMap in there, rather than reusing the first object and .put() changing its values for both Vegetables?
Second related question is.... should I want to actually have 2 vegetables share the exact same list of properties (the same HashMap object), how would I do that? And should this actually be a horrible idea... why? How would wanting this show I just don't know what I'm doing?
My understanding hits a wall beyond "it has to do with object references".
Thanks for helping me clear this up.
Vegetable class as requested:
public class Vegetable {
public VegetableSpec characteristics;
public String name;
public Vegetable(String name, Map<MyProperty, Object> propertyList) {
this.name = name;
this.characteristics = new VegetableSpec(propertyList);
}
public void display() {
System.out.printf("My name is %s!\n", this.name);
for (Entry<MyProperty, Object> entry : characteristics.properties.entrySet()) {
System.out.printf("key: %s, val: %s\n", entry.getKey().toString(), entry.getValue().toString());
}
}
}
... which made me look at VegetableSpec again (I put it in because the book used a separate Spec class, but I didn't understand why it was necessary beyond adding search capabilities; now I think I see it does 2 things, one is defensive copying!):
public class VegetableSpec {
Map<MyProperty, Object> properties;
public VegetableSpec(Map<MyProperty, Object> properties) {
if (properties == null) {
// return a null = bad way to signal a problem
this.properties = new HashMap();
} else {
// correction above makes it clear this isn't redundant
this.properties = new HashMap(properties);
}
}
}
It sounds like the constructor for Vegetable is making a defensive copy. It is generally a good idea to do this to prevent anyone from changing an object in ways the designer of the object does not want. You should (nearly) always be making defensive copies.
I want to actually have 2 vegetables share the exact same list of properties (the same HashMap object), how would I do that?
Pass the same hash map in, and ignore the fact that it makes a defensive copy, should not matter to you as a consumer.
Working on a project in school, I'm a beginner to programming and I have big problems with the making of Bubble Shooter, I need to get all of the balls of the map before it changes to map2..
Trying to make it with listing all of the balls but the program crashes at the end of the first map and gives us the error-report that it can't load a negative value. I figured it was when it was trying to load the gun and wanted to put an if-statement that says that if "allowedBallTypes != null" or zero, as it might be, than it should load the gun.
cannot find symbol - getAllowedBallTypes();
greenfoot java method class
The bubbleworld class:
public BubbleWorld()
{
super(Map.MAX_WIDTH*Map.COLUMN_WIDTH, Map.MAX_HEIGHT*Map.ROW_HEIGHT, 1,false);
// Max speed. We use time-based animation so this is purely for smoothness,
// because Greenfoot is plain stupid. I can't find a way to get 60 Hz so this is
// what we have to do. Note: Exporting the game seems to cap this to some value < 100. :(
Greenfoot.setSpeed(100);
// Load the map.
map = new Map(this, testMap1);
// Update the allowed ball types. (i.e. we don't want to spawn a
// certain color of balls if the map doesn't contain them!)
map.updateAllowedBallTypes();
// Create the cannon.
Cannon cannon = new Cannon();
addObject(cannon, getWidth()/2, getHeight());
The map class:
public int[] getAllowedBallTypes()
{
return allowedBallTypes;
}
public void updateAllowedBallTypes()
{
int allowedCount = 0;
boolean[] allowed = new boolean[Ball.typeCount];
// Only ball types that exist in the map RIGHT NOW as attached balls will be allowed.
for(Cell c : cells)
{
if(c != null && c.getBall() != null && c.isAttached())
{
int type = c.getBall().getType();
if(!allowed[type])
allowedCount++;
allowed[type] = true;
}
}
allowedBallTypes = new int[allowedCount];
int writeIndex = 0;
for(int type = 0; type < Ball.typeCount; ++type)
{
if(allowed[type])
{
allowedBallTypes[writeIndex++] = type;
}
}
}
The Cannon class:
private void prepareBall()
{
// Get a random ball type from the list of allowed ones. Only balls currently in the map
// will be in the list.
int[] allowedBallTypes = ((BubbleWorld)getWorld()).getMap().getAllowedBallTypes();
int type = allowedBallTypes[Greenfoot.getRandomNumber(allowedBallTypes.length)];
// Create it and add it to the world.
ball = new Ball(type);
getWorld().addObject(ball, getX(), getY());
}
Assuming you are getting that error in the pasted snippet of the Cannon class, the error suggests that there is a problem with the getMap() method of BubbleWorld -- can you paste that in so we can see it? It might not be returning the correct type. In general, you need to paste in more code, including complete classes, and say exactly where the error occurs. An easier way might be to upload your scenario with source code to the greenfoot website (www.greenfoot.org -- use the share function in Greenfoot, and make sure to tick the source code box) and give a link to that.
Based on your code, your map class doesn't seem to have a class-level variable declaration of int[] allowedBallTypes;