Objects

In this assignment, you will build a map editor for a side-scroller video game.

  1. Make a backup copy of your previous assignment. If you do not already have a habit of regularly backing up your work, develop that habit now. These days, storage space is cheap. No one regrets making backups. You have been warned.


  2. Make a class named "Tube". Add member variables for the horizontal and vertical position of the tube. (I would call these variables "int x;" and "int y;".) Add two parameters to your Tube constructor to initialize its position. Here is a picture of a tube:


  3. Clean up. If you still have a button or a turtle in your program, you can remove those. When you program, it is important to stay organized. Always take time to use meaningful variable names, indent your code properly, add comments, and keep it readable. I am not going to evaluate at your code. This is for you. My assignments build on top of each other. If you make a complicated mess, you are the one who will have to deal with it later.


  4. Make your model hold an ArrayList of Tubes. (Use your favorite search engine to search for "java ArrayList". Read about the "add", "get", "remove", and "sort" methods.) Add a member variable to your model like this:
    	ArrayList<Tube> tubes;
    
    Initialize it in your model constructor like this:
    		tubes = new ArrayList<Tube>();
    
    Also, let's add a Tube to the Arraylist:
    		Tube t = new Tube(400, 300);
    		tubes.add(t);
    


  5. Make your View draw all of the tubes.
    		for(int i = 0; i < model.tubes.size(); i++)
    		{
    			Tube t = model.tubes.get(i);
    			g.drawImage(tube_image, t.x, t.y, null);
    		}
    


  6. Add a tube wherever the user clicks. (If the user clicks 5 times, there should be 5 tubes on the screen, in each of the places where the user clicked.) Also, get rid of that one tube that we initially put in the ArrayList. That was just there so you could see if the previous step worked.


  7. Add a method to detect when the user clicked on an existing tube. Add a method to your Tube class that takes two parameters, the x and y position where the user clicked. Return true iff the user clicked on the tube. Adjust your program such that when the user clicks on an existing tube, it is removed. A new tube will only be added when the user clicks where there is no tube.


  8. Enable the View to scroll over a larger model. Add a variable to the View class to store a scroll position. When you draw a tube, subtract the scroll position from the horizontal position of the tube. For example:
    			g.drawImage(tube_image, t.x - scrollPos, t.y, null);
    
    Adjust the scroll position value when the user presses the left or right arrow keys. (If the scrolling is painfully slow, increase the scrolling step size to your liking.)


  9. When the user presses 's', save your model to a Json file. Add this file to your project: Json.java. Examine the comments, which include an example of how to use it. Add a "marshall" method to your Tube class that marshals the object into a Json node. Also, add a "marshall" method to your Model class that marshals the object into a Json node. Use the Json.save method to save the Json node to a file named "map.json".


  10. When the user presses 'l', load the model from a Json file. Add an unmarshaling constructor to your Tube class. Also, add an unmarshaling constructor to your Model class. Use the Json.load method to load the Json node to a file named "map.json". After you make a new model, you will need to tell the other classes about it. A good way to do that is to add setters to the other classes, like this:
    	void setModel(Model m)
    	{
    		model = m;
    	}
    


  11. Keep your ArrayList sorted. Whenever you add a new Tube to your model, sort the ArrayList according to the horizontal positions of the tubes. To do that, you will need to write a Comparator, like this:
    import java.util.Comparator;
    
    class TubeComparator implements Comparator<Tube>
    {
    	public int compare(Tube a, Tube b)
    	{
    		if(a.x < b.x)
    			return -1;
    		else if(a.x > b.x)
    			return 1;
    		else
    			return 0;
    	}
    
    	public boolean equals(Object obj)
    	{
    		return false;
    	}
    }
    
    (Since this class is not public, you do not have to create a separate file for it.)


  12. Make your View efficient. Iterating through all the objects in your entire map every time you want to draw the screen is not a very scalable solution. Let's make it more efficient by only visiting the portion of the map actually on the screen. Here is some code that uses binary search to find a good starting point:
    int findFirstTubeOnScreen()
    {
    	int start = 0;
    	int end = tubes.size();
    	while(true)
    	{
    		int mid = (start + end) / 2;
    		if(mid == start)
    			return start;
    		Tube t = tubes.get(mid);
    		if(t.x - scrollPos < -100)
    			start = mid;
    		else
    			end = mid;
    	}
    }
    
    Adjust your drawing loop to start there and stop when it finds an object that is right of the right side of the screen. (Now you can make your map as big as you want without significantly slowing down your program.)


  13. Test your code. Make a zip archive or tarball containing your source files, image files, and a working build script. Do not submit any .class files or .jar files. Your build script should build but not execute the program. Test your zip file before you submit it by extracting into a separate folder and running your build script. Finally, click on the "Project submission" link on the main class page to submit your archive file. If you have any difficulties with the submission server, please e-mail your submission directly to the grader.


Rubrick:

If your code builds and runs on the grader's first attempt, behaves as described, and correctly implements all of the required features, you will receive full credit (minus any late penalty). If the grader notices any problem, he will notify you by e-mail and give you no credit until you submit code that he or she determines to meet the requirements of this assignment. If your code is messy, that's your problem, and you will suffer the natural consequences of having to deal with your own code in subsequent assignments that build on top of this one. Mwa ha ha ha ha ha ha ha!

FAQ:

  1. Where can I get help?
    A: You can try the automated professor. You can ask on the class forum. You can ask the grader. You can e-mail the grader. You can show up to my office hours. You can ask Google, Siri, or Alexa. You could search for a tutorial on Java. You could buy and read the book.


  2. Q: How does one use a try/catch block?
    A: If you must use a try/catch block, make sure the catch part does not hide any errors. A good way to inform the user about an error is to print a stack trace and terminate the program. (If you don't terminate the program, you might not even notice that something bad happened.) Here is an example of a well-crafted try/catch block:
    try
    {
    	bird.image = ImageIO.read(new File("tube.png"));
    }
    catch(Exception e)
    {
    	e.printStackTrace(System.err);
    	System.exit(1);
    }
    
    Here is an example of the wrong way to use a try/catch block:
    try{
    	bird.image = ImageIO.read(new File("tube.png"));
    } catch(Exception e) {}   // BAD, BAD, BAD!!!!!!
    
    Also, it is a good idea to put as little code as you can inside the try section.