0% found this document useful (0 votes)
16 views22 pages

FinalProjectCheckpoints0-1

Uploaded by

Bo Stevens
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
16 views22 pages

FinalProjectCheckpoints0-1

Uploaded by

Bo Stevens
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 22

CS-2810 Final Project

Project Description
We are finally tackling our final project! Before we get to rolling up our sleeves however, there are a couple things you should know
before getting started.

What ’s the project about?


We’re going to be building a “simple” image editing application. Users will be able to load up images, doodle on them, and perform a
couple of very basic edits, then save their new image back out to a file. To do this, we’ll be using a Java graphics library called LibGDX.
We’ll also be building lots and lots of custom functionality along the way.

Project Philosophy
Throughout this project, we will be creating many common pieces of program functionality by hand. This is NOT because this
functionality doesn’t already exist somewhere in Java or inside our library. We’ve chosen a relatively simple application to build
(image editor) specifically to make a mountain out of a molehill. What this means is that many aspects of this project are going to be
blown up intentionally to be a little more difficult than they would normally be.

Why?
Typically, the content taught by this course is not commonly applied in most junior and mid-level software engineering positions out
in the real world. For the tasks you’ll be asked to solve at these levels, only a basic understanding of memory hierarchy and system
performance is required. Any program or code where it would be “valid” to use the approaches taught in this course directly instead
of using a pre-built solution would typically be inordinately complicated, and likely outside the reach of you and most of your peers.

We ARE making this particular program more difficult than it absolutely needs to be.
We are NOT building anything close to the difficulty of a real-world example

-Hopefully that is a fair enough concession to make

Last Note:
The approaches we will be taking in this program were not always selected because they are the cleanest, neatest, or best performing ways
of solving the problems will be encountering. The purpose of this program is to explore a variety of programming topics and problem-solving
methodologies. It is not necessarily recommended that you make use of these methods in a real-world software engineering environment.

That said, a concerted attempt was made to explain how to build good software. Within this project should be a number of illuminating
sections on Object Oriented design, program structure, language deficiencies, packages/namespaces, UI design and programming, and
more.

Generally speaking, it’s okay not to pull your hair out over the technical details of WHAT we are doing
Instead, shift your effort more towards understanding the WHY of what we’re doing instead

Checkpoints
0. Checkpoint 0
1. Checkpoint 0.5
2. Checkpoint 1
Checkpoint 0

Checkpoint Objectives:
• Find a partner
• Download LibGDX configuration tool
• Download Github Desktop (Or use cli if you’re already proficient in Git)
• Generate a new respository
• Generate a new project inside the repository with the LibGDX tool
• Add a screenshot of your program running, and the console printing out the names of you and your partner
• Push the project to your Git repo

NOTE: This entire checkpoint is going to be about setting up your project. This takes a non-trivial amount of time. It is HIGHLY
recommended that you start this checkpoint as early as possible, in case you run into issues getting the project configured.

0.0 Setting up your Github Repository

For those experienced with source control, feel free to skim

For many of you, this might be your first larger project. Building larger software can be much more complicated than the smaller
programs you might be used to working on for school projects. Switching between machines as needed becomes a hassle,
overcomplicated/poorly written code will become difficult to remove after wiring it into your codebase, collaboration will become
difficult between everyone working on the project together.

To fix these issues, and hopefully make your experience with the project a little less painful, we will take advantage of the version
control software Git.

What is Git?
Git is a piece of software that allows us to save the current state of our codebase and upload it to a repository. This repos itory keeps a full
history of changes made to the codebase, and can also be split into multiple branches. These branches make it ea sy for multiple people to
work independently on the same project at the same time, and can be combined (or merged) to bring everyone’s changes together

Git is traditionally used through the command line, where we can type in commands to control our repository. While this is the
traditional and most foundational way of working with Git, it can be a little intimidating to people seeing it for the first time.

Source: https://stevenwilliamalexander.wordpress.com/2017/07/26/why -you-should-use-git-command-line/

So for this project, instead of using Git the traditional way, the instructions in this manual will be written to utilize Github Desktop
instead as it offers the same functionality, but utilizes a full GUI to make things easier to get into.

Git vs. Github vs. Github Desktop


This is a common point of confusion for many people new to version control. Git is the core version control software. Github is a service
separate from Git that allows you to host Git repositories online. Github Desktop is a piece of software built by Gith ub that allows you to
create and manage Git repositories with a GUI.

To get started, you’ll need a Github account. You can create one on the official Github website.
https://github.com/
Make sure you have the base software of Git installed on your machine as well.
https://git-scm.com/downloads
And finally, after you’ve done that, go ahead and download Github Desktop and run it.
https://desktop.github.com/
Once you finally have everything working, our first step is going to be to create a new repository on our machine. You can do this by
pressing Ctrl + N, or going to File -> New Repository

I thought you said Github stored repositories online


You’re absolutely right! Github stores repositories online. They are, however, created and managed locally, on our machines. When we
change our codebase, we’ll be affecting our local repository. Then, periodically, with Github Desktop, we’ll send all of our updated code
out to the repository stored online.

Call your repository Image Editor, set the Git ignore to Java, and set the local path to wherever you want your project to be stored on
your machine.

What’s a Git Ignore?


By default, when we upload our repository to Github, it will include everything located inside our Local path. However, in ma ny projects,
there are files we don’t really need to be uploading. Temp files for example, in larger projects, can become Gigabytes in size. A Git ignore
file describes all of the file types we never want to upload to our repository. You can create or edit this file manually, bu t Github Desktop
has a lot of handy premade Git ignores that we can take advantage of. The Java Git ignore f or example, ignores all files with the extension
of .class as they’re always generated locally.

Local Path is the directory your repository is going to be located inside of. Set it wherever you want

Click Create repository, and you should find a new folder created in your Local path called Image-Editor. If you open it, you should see
two files called .gitattributes and .gitignore. This will mean that you have successfully created your repository!

We will come back around to Github again once we finish off this section by generating our project. If you can see the above image in
your file explorer, you should be good to move on.

0.1 Picking a graphics library


Before we start building anything ourselves, we need to set up our environment. While we absolutely COULD write this project with just
a base installation of Java, we wouldn’t WANT to do so. The required knowledge to do this would not only be well beyond the scope of
this class, but possibly beyond the scope of even a master’s level CS course.

Graphics systems are COMPLICATED!

Ideally, we’re going to want to rely upon the compromise of using a pre-built library that is going to do a lot of the heavy lifting for us.
When it comes to Java graphics libraries, there are many. While I won’t be able to cover all of them, here are a couple.
• Java Abstract Window Toolkit (AWT)
o Released 1995
o The base graphics package of Java
o Only suited for minimal/simple/legacy GUI development
• Java Swing
o Released 1998
o Built off of AWT
o Provides a more feature rich experience compared to AWT. Built off a component based system (Buttons, fields, boxes,
etc) Is generally considered deprecated by its predecessor
• JavaFX
o Released 2008
o Built as a more modern replacement for Swing
o Still largely seen as the modern standard for Java GUI programming today
▪ Has been discontinued by Oracle however
▪ Modern JavaFX is no longer a part of the standard Java installation
▪ Not terribly popular for consumer applications due to constraints that lead to an outdated looking style
▪ Most commonly seen in B2B (business to business) applications

All of the above-mentioned graphics libraries are AWESOME … but, we’re not going to be using any of them for this project, for the
following reasons.

• Heavy automation
o They are very easy to use…
o All of the above libraries however, hide levels of functionality that pertain to the material of this course
• Deprecated
o The above libraries are all largely considered deprecated, for a variety of reasons
▪ They ARE still used in lots of commercial software
▪ But this has a high likelihood of changing in the future
• Abstracted
o These libraries all make GUI programming incredibly EASY
▪ But this is only achieved by displacing the programmer very far away from what their code is actually doing
o A (flawed?) premise
▪ Even if we only ever perform GUI programming at this abstracted layer professionally
▪ Knowing what our code is doing underneath the hood will empower us to take firmer control over our code, and
make it do much more interesting and rigorous activities
• This is a bit more of a personal opinion though, so it’s left here at the bottom

Because of the above reasons, we will be using a moderately lower-level graphics API for our final project, LibGDX.

What is LibGDX?

LibGDX is an open-source game development framework, built off of the Java graphics library LWJGL (Light Weight Java Game Library),
a Java based wrapper for OpenGL. LibGDX contains a large number of classes and methods that will help get us started in building a
moderately (for as far as this class is concerned) low-level application

I thought you said we were removing abstraction? What’s with the word soup?
We are removing SOME abstraction. Pure graphics code would be well beyond the scope of a 2000 level course

A game-development library? Really? I thought we were building an image editor


All graphics technologies are built on the same basic principles. Easy -to-use technologies like JavaFX, HTML, and others all attempt to hide
the complexities of graphics. Game -development tech, while still abstract, reveals much more of the underlying syst ems that go into
making graphics technologies function. For our purposes, LibGDX is just a well-polished library that is going to give us access to some of
those lower-level ways of interfacing with a graphics system

1.1 Configuring LibGDX


The first step in our project, before we write any code, is to configure our project to use LibGDX. Luckily for us, there is a convenient
tool available that does this for us. While I will be giving a breakdown of the steps involved, there is also a fantastic article on the
LibGDX website itself as well, if you would rather.
https://libgdx.com/wiki/start/project-generation

If you follow the link above, the first step in the official guide will be to download the LibGDX setup tool. Open it and configure it as
shown below.

Set your Output folder to the directory you created for your Github Repository

Once you’ve done this, click Generate, and your project will be created for you. You can verify this by checking Github Desktop. On the
left-hand side, you should see a list of new folders and files has shown up, with a Green “+” next to each of them, marking them as
having been added to the local repository.

Now that we have successfully generated our project, we are going to import it into our IDE, so we can actually work with it.
NOTE: For the duration of this project, it will be assumed that you are running the project in Eclipse. It is highly recommended th at you do
so as well, but you can complete this project in any IDE of your choice

To do this, open up Eclipse and in the top left, go to File -> Import. LibGDX uses Gradle to manage our project, so we’re going to go to
Grade -> Existing Gradle Project, and click next.

What is Gradle?
Gradle is a build tool. It automates and manages various parts of the development process. For our purposes, it isn’t actually terri bly
important what Gradle does as we won’t be interfacing with any of its major features. Just trust that it’s doing nice things for us under the
hood.
Now, when prompted, set the Project Root Directory to wherever your Image-Editor folder is located, and click Finish. Your auto-
generated codebase should now import into Eclipse.

The first thing you might notice is that our code doesn’t import as one, but actually three separate projects. The way LibGDX projects
are built, each platform we want our program to work on will be added as its own project. This is done because the same program will
have a lot of differences between its desktop version and mobile version. For our purposes, the vast majority of work that we are going
to do will be in the ImageEditor-core project.

To test and make sure that your project is actually working, open up the desktop project and try to run DesktopLauncher.java and it will
open up the default project screen.

A bit ugly isn’t it?

If you can get this screen to show up, then your project is properly configured! Now we just have a little bit of wrapping up to do, and
we should be good.
0.2 Pushing our changes

Before we push all of our code out to our repository on Github, add this print statement to your main method, and put the names of
yourself and (if applicable) your partner, then take a screenshot of the default project running, and both of your names printing out in
the console.

Save this, you’ll be submitting it at the end

Once this is done, head back to Github Desktop. We’re going to push all of the changes we’ve made to our online repository. The first
thing that we are going to do is fill out a Summary and description of what we’ve done.

Fill these two sections out however you desire

Once you’ve done this, click “commit to main”, then go up to the top-right and click “Publish repository”. It will bring up a dialogue box.
Leave the setting as default and click “Publish repository” inside this dialogue box. Don’t worry too much just now what we did. We’ll
come back and explain the process again later.

If you did this correctly, you should be able to hop onto a web browser and log in to your Github account, and see your repository has
now been uploaded to the website.
If you ever want to download your project on a new machine, open Github Desktop and go to File -> Clone Repository. You can then
select your Image-Editor and clone the most recent version of the project onto your computer. Doing it this way is not only pretty nifty
and convenient, but also downloads all of the files needed for Git to track file-changes.

Those new to Github are likely to discover the “Download ZIP” button and be tempted to use that instead.

NEVER USE THIS!


Git tracks changes made to files, making it control what gets uploaded to the repository. Downloading as a ZIP
removes all of the files that mange file-tracking. This breaks one of the best features of version-control software

Those new to Github are also likely to discover the “Add file” button. This might be tempting to use as it allows users to upload files
directly to their Github repository directly, without having to interface with the underlying software of Git.

NEVER EVER USE THIS!


This both breaks and bypasses even more features of Git. If you want to upload/download files to your repository,
PLEASE PLEASE PLEASE go through Github Desktop

If you can see all of your files inside your Github repository, take a screenshot of the repository as well (like the one I showed above).
Go to Canvas and submit the picture of your project running, and the picture of your github repository as well.
Checkpoint 0.5

0.5 Understanding the Codebase


This Checkpoint is technically optional BUT highly recommended

Our first look at our codebase is going to be intimidating at first. Take a breath and go slowly. By the end, we’re going to understand this
project is gonna feel just like home.

Note: This project is going to be dramatically more complicated than anything we have discussed before. As such, there are times that
the instructions in this manual are going to occasionally handwave certain pieces of functionality. Do not be afraid when this happens.
If an explanation leaves you confused or wanting, please check the LibGDX documentation. Otherwise, try not to sweat the minor
details. If a piece of functionality is ever handwaved, it is generally done so because it will either be unimportant or not worth going
into further detail upon.
https://libgdx.com/wiki/

Like with every program, we’ll start by looking at the main method to see if we can get an understanding of the life-cycle of our
program.
public static void main (String[] arg) {
Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
config.setForegroundFPS(60);
config.setTitle("ImageEditor");
System.out.println("Project made by: ______ and _____" );
new Lwjgl3Application(new ImageEditor(), config);
}
Reminder – Main is located inside Image Editor-desktop -> DesktopLauncher.java

As mentioned earlier in Objective 0 LibGDX is built off the back of another graphics library known as lightwight java graphics library. The
code here just sets up the broader context of our program and deals with the LWJGL side of things. We’re not going to touch much
here, but there are a couple lines of interest worth mentioning.

config.setForegroundFPS(60);
Controls the framerate of our application (How many times per second we attempt to update the contents of the screen)

config.setTitle("ImageEditor");
Sets the name of our application window. We can see the following effect by editing the String in the method call

And finally, we can see that our main method is ended with the following method call

new Lwjgl3Application(new ImageEditor(), config);


You can try hitting f3 to peek into the definition of Lwjgl13Application. But even just a quick look will likely prove pretty discouraging

Perplexingly, our main method hasn’t really revealed very much about our program at all. This is because the bulk of our program is
actually stored in ImageEditor-core. Let’s take a look and open ImageEditor.java.

public class ImageEditor extends ApplicationAdapter {


SpriteBatch batch;
Texture img;
@Override
public void create () {
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
}
@Override
public void render () {
ScreenUtils.clear(1, 0, 0, 1);
batch.begin();
batch.draw(img, 0, 0);
batch.end();
}
@Override
public void dispose () {
batch.dispose();
img.dispose();
}
}

Now this code may still be a mystery to us at the moment, but does look a bit less intimidating hopefully. Our default program exists in
three parts, and that’s how we’ll attempt to break it down.
Entry Point:
public void create () {
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
}
create() can be considered as the entry -point for the core part of our project

In our default program, we’re creating something called a SpriteBatch, which we’ll discuss in a moment. We can also see that we’re
loading in a jpeg image from somewhere. Oddly, we’re not giving a direct file-path, so where is this image located? We can find it
under Image-Editor -> assets.

All files that we want our project to use will be placed in our assets folder

We can actually experiment a little here by putting an image in our assets folder and trying to load that image instead, and running our
program.

By default, LibGDX is capable of handling most image file -types. Try setting this up with some image on your own computer, and running
the program.

This image is stored as a Texture. We don’t know a lot about it, but for now, it is safe to assume for now that a Texture is something
that allows to show data to the screen. Let’s try taking a look at our next method.

Update method:
public void render () {
ScreenUtils.clear(1, 0, 0, 1);
batch.begin();
batch.draw(img, 0, 0);
batch.end();
}
render() is called every time our program wants to refresh the contents of the screen

Right now, you are probably reading this manual on a computer. Go ahead and wiggle your mouse. What happens when you do this?

Well, obviously, the mouse on your screen moves in the same direction as your arm. Intuitive right? But how did this happen? This
behavior is probably so intuitive and ingrained in your muscle memory, that you might not have ever thought about this before.
Somewhere, perhaps buried deep inside our operating system’s codebase, there lies some code that possibly looks something like this.

class Mouse{
public int XPosition, YPosition;
}
Feel free to imagine what other code might exist in this theoretical class as well
Ah, that makes a bit of sense. If our mouse has an x and y position, then all we have to do is somehow detect when the mouse has
been moved. We can then do something like the following

public void mouseMoved(int xMovement, int yMovement) {


XPosition += xMovement;
YPosition += yMovement;
}

Now all we have to do is show our mouse moving across the screen. But…how do we show movement?

I’ll reveal the trick here a little early and ruin the surprise a bit. We DON’T! Our computer is actually incapable of showing anything
move. Instead of moving, it’s more accurate to say that things like our mouse cursor teleport. Our display refreshes on a set interval.
Whenever this happens, everything currently on the screen disappears. Then, we go back and re-draw in everything. If the user moved
the mouse in this short time-interval, the mouse will now be drawn to a different part of the screen. If we refresh our screen often
enough, it will “look” like our mouse is actually moving when it’s not!

Our screen clears, our background is redrawn, and our mouse is drawn in a new position
Note: This is a simplification of this process, not all graphics libraries draw in items starting from the back to the front like this

This is what our render() method is doing. Remember back in DesktopLauncher, how there was a line of code that determined how
often the program would “refresh” itself?

config.setForegroundFPS(60);
This one!

Well, by default, this line tells our program to “refresh” 60 times per second. And every time we want to “refresh”, our program calls
render()

So what does render() do then?

Our method starts by clearing the entire screen, and flooding it with a single color.

ScreenUtils.clear(1, 0, 0, 1);
This sets the full contents of the screen to black

Now obviously, I just told you what this code does, but how could we determine this for ourselves? Well, a couple ways actually. The
first and simplest, is to just mouse over the method-name itself. This will bring out a pop-up that gives us some information about
what this method does.

We can also find the documentation page here -


https://javadoc.io/doc/com.badlogicgames.gdx/gdx/latest/com/badlogic/gdx/utils/ScreenUtils.html

And perhaps the most fun way of testing this is to comment out all of the lines below and run our program again.
public void render () {
ScreenUtils.clear(1, 0, 0, 1);
// batch.begin();
// batch.draw(img, 0, 0);
// batch.end();
}
But lets also try messing with our method-call as well. The screenshot above says that the first three numbers represent how much
red, blue, or green we are going to flood the background with. Try setting each of these three numbers to any number you want
between 0 and 1.

Flooding the program with purple by setting the background color to nearly equal parts red and blue

So what is the rest of our program doing?

To handwave a little, our SpriteBatch “batch” is responsible for drawing everything else to the screen.
batch.begin();
batch.draw(img, 0, 0);
batch.end();
We need to tell our SpriteBatch to begin a new batch whenever we want to draw something, and to end a batch when we’re done.
-If this sounds confusing, don’t worry, we’ll be playing with this later

Closing out the program:


public void dispose () {
batch.dispose();
img.dispose();
}
This last method is the simplest. It is called when our program wants to exit. It closes out our Texture and SpriteBatch before the
program closes for good

Checkpoint 1
Checklist:
• Draw a Texture to the screen
• Draw a rectangle to the screen manually
• Create Rec2D class
• Switch renderer to use Rec2Ds
• Add Velocity to Rec2D
• Make Rec2D bounce off the side of the screen
• Make Rec2D change to a random color when it bounces

1.0 A Familiar Sight


Our final goal of a fully featured image editing application is still far off, but every journey starts with its first step. Our first step will be
recreating an old sight from childhood (Hopefully at least. Otherwise, I fear I’m getting a little old)

https://www.youtube.com/watch?v=5mGuCdlCcNM
Bouncing DVD Logo

By recreating this simple simulation, we’ll engineer the core structure around which our entire future program will revolve. This is all
going to begin with our first custom class (of many yet to come)

1.1 Drawing to The Screen


How can we go about drawing something new to the screen? Luckily, this process is not only pretty simple, but is already
demonstrated by our basic starter program, and goes as follows-

• Create a Texture when our program starts


• Each time render() is called, use a SpriteBatch to draw that texture to the screen
Let’s demonstrate this by adding a new Texture to our program.
Texture img;
Texture secondImg;
@Override
public void create () {
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
secondImg = new Texture("badlogic.jpg");
}
@Override
public void render () {
ScreenUtils.clear(.4f, 0f, 0.5f, 1);
batch.begin();
batch.draw(img, 0, 0);
batch.draw(secondImg, 0, 0);
batch.end();
}
Lines highlighted in blue are lines that have been added to our program

If we run our code though, we’ll see something a little disappointing. Our new Texture doesn’t seem to be showing up at all. This isn’t
because something is wrong with our program. It looks like we’re only drawing a single Texture because both img and secondImg are
being drawn to the exact same location on screen.

Overlapping textures

Luckily, there’s a pretty easy way to move our second Texture so it’s not overlapping our first anymore. Batch.draw() takes in arguments
that correspond to an X and Y coordinate. If we edit our code, we can make our program able to show both Textures at the same time.

public void render () {


ScreenUtils.clear(.4f, 0f, 0.5f, 1);
batch.begin();
batch.draw(img, 0, 0);
batch.draw(secondImg, 350, 0);
batch.end();
}
This code will draw the second Texture 350 pixels to the right of the origin point of our program (bottom left corner)

Image drawn at (0,0) and (350, 0). All items are drawn with their position representing the bottom -left of the image.
Because we’re just drawing the same image twice, we don’t actually even need two textures in this case. We can delete secondImg
entirely, and our code will do the same thing.

SpriteBatch batch;
Texture img;
Texture secondImg;
@Override
public void create () {
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
secondImg = new Texture("badlogic.jpg");
}
@Override
public void render () {
ScreenUtils.clear(.4f, 0f, 0.5f, 1);
batch.begin();
batch.draw(img, 0, 0);
batch.draw(secondImg, 350, 0);
batch.draw(img, 350, 0);
batch.end();
}
Lines highlighted in RED are lines that should be deleted from our program

1.2 Rectangles
In computer graphics, shapes are the fundamental building blocks of everything that we create - Triangles, squares, circles, oblongs,
etc. By combining basic geographic shapes together, we can recreate any kind graphics our imagination desires.

But if we want to draw whatever our imagination desires, we’re going to have to write a LOT of code

So instead, for our program, we’re going to create a special limitation to reign in the scope of our project. EVERYTHING that we draw to
the screen is going to be a rectangle. That means every button, menu, and item in our application is going to be some kind of
rectangle. As we build out this project, we’ll see that this limitation is going to make our program a whole lot simpler to build.

So let’s start by trying to draw a rectangle to the screen!

One way we COULD attempt to approach this problem is to rely on our current understanding of Textures and accomplish this task by
finding a rectangular image, and loading it into a Texture.

Like this green rectangle right here for example!

public void create () {


batch = new SpriteBatch();
img = new Texture("Rectangle.png");
}
@Override
public void render () {
ScreenUtils.clear(.4f, 0f, 0.5f, 1);
batch.begin();
batch.draw(img, 0, 0);
batch.draw(img, 350, 0);
batch.end();
}
Rectangle.png has been placed in our assets folder already

Hurray! A green rectangle!

But what happens when we want to change the color of our current rectangle? What do we do if we want to try to change the shape
of the rectangle as well?

We have to go and change the ORIGINAL image in another piece of image editing software. This would be incredibly cumbersome to
do every time we want to draw a different colored or shaped rectangle.
So what can we do?
Instead of loading a pre-existing image into our Texture, we can just create our Texture manually
1.3 Pixmaps and Textures
We’ve talked about Textures and fair bit so far, and we understand that they are important for drawing things to the screen, but what is a
Texture? Let’s knock out a couple definitions real quick.

Pixel: Computer displays are made of millions of tiny little lights. Each of these lights can shine any color we want. In the software world, a
Pixel is the smallest unit of data that can make one of those lights shine a given color. The color of a pixel is given by its ratio of red, blue, and
green light.
Texture: A Texture is a data structure that holds a bunch of pixel data. Textures are the simplest way of telling our computer how to draw
something to the screen. The computer can loop through all of the pixels in the texture, and light up corresponding lights on the display with
the color given by that pixel data.

If a Texture is just a container for color data, then could we somehow generate our own Textures from scratch?
Can AND will!

Pixmap: A data structure given to us by LibGDX that allows us to generate color data that can be sent to a Texture

https://libgdx.com/wiki/graphics/2d/pixmaps

If we want to create custom Textures, a Pixmap will be the simplest way for us to do just that. Let’s go ahead and look at the constructor we’ll
be using for a Pixmap, then see if we can make one.

public Pixmap (int width, int height, Format format) {


Constructor for a Pixmap

We can see that if we want to create a Pixmap, all we have to do is give it a width and a height.

But what about that thing in the constructor, Format?


Format in this case refers to color format. We won’t worry about this so much and will be defaulting to Format.RGBA8888 for t his entire
project.

Let’s try to create a Pixmap and see how they work!

Pixmap rectangleMap;
public void create () {
batch = new SpriteBatch();
img = new Texture("Rectangle.png");
rectangleMap = new Pixmap(200, 100, Format.RGBA8888);
}
Creating a Pixmap that is 200 x 100 pixels

We can now make img use our new Pixmap as the source for all of its pixel data by simply creating a new Texture, and sending in our Pixmap in
the constructor.

rectangleMap = new Pixmap(200, 100, Format.RGBA8888);


img = new Texture(rectangleMap);

But if I run the program now, no rectangle shows up on screen anymore :(


This is just because we haven’t put any color data into our Pixmap

We can tell set the color of all the pixels in our Pixmap with the following code
rectangleMap = new Pixmap(200, 100, Format.RGBA8888);
rectangleMap.setColor(Color.BLACK);
for(int x = 0; x < rectangleMap.getWidth(); x++) {
for(int y = 0; y < rectangleMap.getHeight(); y++) {
rectangleMap.drawPixel(x, y);
}
}
img = new Texture(rectangleMap);
Sets the color we’ll be drawing with to black, then loops through every pixel in the Pixmap and draws that color to the pixel
If we run our code now, we should be able to see that our program now displays a black rectangle at the bottom of the screen!

And you can change both the color and shape of the rectangle by just playing with the values we’ve already created

rectangleMap = new Pixmap(50, 500, Format.RGBA8888);


rectangleMap.setColor(Color.GREEN);

A VERY tall green rectangle

1.3 Rec2D
Now let’s say that we want to scale things up a little, and draw TWO DIFFERENT rectangles to the screen at the same time. We could
replicate the code we’ve already created, but try and guess what I’m going to say is wrong with the following code.

SpriteBatch batch;
Texture img;
Texture imageTwo;
Pixmap rectangleMap;
Pixmap otherRectangleMap;
@Override
public void create () {
batch = new SpriteBatch();
rectangleMap = new Pixmap(50, 500, Format.RGBA8888);
otherRectangleMap = new Pixmap(300, 50, Format.RGBA8888);
otherRectangleMap.setColor(Color.BLUE);
rectangleMap.setColor(Color.GREEN);
for(int x = 0; x < rectangleMap.getWidth(); x++) {
for(int y = 0; y < rectangleMap.getHeight(); y++) {
rectangleMap.drawPixel(x, y);
}
}
for(int x = 0; x < otherRectangleMap.getWidth(); x++) {
for(int y = 0; y < otherRectangleMap.getHeight(); y++) {
otherRectangleMap.drawPixel(x, y);
}
}
img = new Texture(rectangleMap);
imageTwo = new Texture(otherRectangleMap);
}
@Override
public void render () {
ScreenUtils.clear(.4f, 0f, 0.5f, 1);
batch.begin();
batch.draw(img, 350, 0);
batch.draw(imageTwo, 100, 200);
batch.end();
}
Code drawing two intersecting rectangles

This approach doesn’t SCALE well at all. Every rectangle we want to add to our application, we’ll have to write 11 new lines of code. If I want to have
20 rectangles on screen at once, I’ll have to write out 220 lines of code!

But what can we do about that?


We can move all of the data we’re using to represent a rectangle into its own class
Start by creating a class called Rec2D inside ImageEditor-core

package com.mygdx.imageeditor;

public class Rec2D {

Didn’t you tell us a couple assignments ago never to shorthand names?


Short-handing is very dangerous, but this class is going to be the core of our entire program, and will be referenced very heavily. For this
reason, it’s a fair enough compromise to want to shorten the name a little

So, what data were we using to represent our rectangles before? Let’s take a look back at the code for drawing a single rectangle, and
highlight every piece of data that determines how our rectangle is drawn to the screen.

public void create () {


batch = new SpriteBatch();
rectangleMap = new Pixmap(50, 500, Format.RGBA8888);
rectangleMap.setColor(Color.BLUE);
for(int x = 0; x < rectangleMap.getWidth(); x++) {
for(int y = 0; y < rectangleMap.getHeight(); y++) {
rectangleMap.drawPixel(x, y);
}
}
img = new Texture(rectangleMap);
}
public void render () {
ScreenUtils.clear(.4f, 0f, 0.5f, 1);
batch.begin();
batch.draw(img, 350, 0);
batch.end();
}
-Width, Height, Color, XPosition, YPosition

We are going to want our rectangles to be able to store a width, length, and position (x, y coordinates)

It might be tempting to program that data in as follows –


public class Rec2D {
public int Width;
public int Height;
public int XPositon;
public int YPosition;
}

Mathematically, you would say that these numbers represent scalar values in the horizontal and vertical axis. While this approach
might work fine on an intuitive level, values that only work as scalars are actual very limited in terms of use when it comes to computer
graphics. Instead of storing this data in basic ints, we’re going to use a data structure inherited from LibGDX, Vector2

I’m sorry, but what?


Yeah, don’t worry about it too much. Just trust that Vectors (like from Physics and Linear Algebra) contain a number of reall y useful
mathematical properties that are very helpful for modelling all kinds of behaviors. For our purposes, they’re mostly just going to be a
neater way of storing things that require an x and y or width and height component

import com.badlogic.gdx.math.Vector2;
public class Rec2D {
public Vector2 Scale;
public Vector2 Position;
}
Vectors for Scale and Position. Each Vector2 contains an X and Y variable we can set

We’re also going to want Rec2D to be able to keep track of color as well. We’ll add a new variable of type Color for this. This one however, we’ll
never need to access from outside of Rec2D, so we are going to make it private. We’ll also need a Pixmap and Texture for each Rec2D as well. This
could be theoretically a little wasteful, but we won’t worry too much about that.

public Vector2 Scale;


public Vector2 Position;
public Texture RecTexture;
private Pixmap _pixelMap;
private Color _recColor;
Private variables are spelled in this project with an _ at the start

Now create a constructor for Rec2D that accepts two Vector2s for scale and position, and a Color for the color of the rectangle as arguments,
and set the variables in our class equal to these arguments.
Let’s start moving code from ImageEditor over into Rec2D. Move all of the code responsible for creating our PixMap into a new method
inside of Rec2D called generateTexture()
public void create () {
batch = new SpriteBatch();
rectangleMap = new Pixmap(50, 500, Format.RGBA8888);
rectangleMap.setColor(Color.BLUE);
for(int x = 0; x < rectangleMap.getWidth(); x++) {
for(int y = 0; y < rectangleMap.getHeight(); y++) {
rectangleMap.drawPixel(x, y);
}
}
img = new Texture(rectangleMap);
}

Note: When moving the code over, you should also remove all of the constant data from this code, and replace it with the new
variables we created in Rec2D

private void generateTexture() {}


Fill this method out with all the code from ImageEditor.create() that creates a Pixmap

Call this method from the constructor of your Rec2D.

If everything went well, you should be able to run the following code –
public void create () {
batch = new SpriteBatch();
rectangle = new Rec2D(new Vector2(200, 100), new Vector2(200,200), Color.RED);
}
public void render () {
ScreenUtils.clear(.4f, 0f, 0.5f, 1);
batch.begin();
batch.draw(rectangle.RecTexture, rectangle.Position.x, rectangle.Position.y);
batch.end();
}

And see the following when you run our program

Red rectangle in about the center of our program

1.4 Movement and Screen-Size


Remember that our original goal was to recreate the bouncing DVD logo of yesteryear. So far, we have come up with a way of drawing objects to the
screen, but if we want to actually recreate this program, we need to make our rectangles capable of moving.

Luckily, this is quite easy!

Add a new public Vector2 to Rec2D called Velocity, and add a new argument to our constructor

Velocity?
Yes! Velocity! Traditionally, velocity is represented by (meters/second) but we’ll use our Velocity variable to represent (pixels/refresh).
Storing it in a Vector2 will allow us to give our Rec2D a velocity in both the x and y directions

public Vector2 Velocity;


private Pixmap _pixelMap;
private Color _recColor;
public Rec2D(Vector2 scale, Vector2 position, Vector2 velocity, Color recColor) {

Now when we create a Rec2D, we’re going to need to give it not only a direction to move in, but a scalar value telling it how quickly it is
going to move in that direction

rectangle = new Rec2D(new Vector2(200, 100), new Vector2(200,200), new Vector2(5,0), Color.RED);
rectangle will have a velocity of 5 pixels/refresh along the x -axis
To make our velocity actually affect our Rec2D, all we have to do is add rectangle.Velocity to rectangle.Position every time we call
.render(). We can do this pretty simply with the method rectangle.Velocity.add() which will allow us to add two Vector2s together

public void render () {


ScreenUtils.clear(.4f, 0f, 0.5f, 1);
batch.begin();
batch.draw(rectangle.RecTexture, rectangle.Position.x, rectangle.Position.y);
rectangle.Position.add(rectangle.Velocity);
batch.end();
}

Run our code and OH NO!


Our rectangle is walking itself off the screen!

This is happening because our program is moving our rectangle to the right INFINITELY, even when it makes the rectangle disappear out of view.
Ideally, what we want to have happen is that our rectangle bounces off the side of the screen, instead of disappearing.

We could do this IF we knew the size of the screen


Consider the following pseudo-code
//if(rectangle.position is out of horizontal bounds)
// rectangle.velocity.x *= -1;

If we are going to disappear out of the right side of the screen, Velocity will become -5 and our rectangle will bounce to the left.
If we’re going to disappear now off the left side, Velocity will become 5 again, bouncing us to the right

So is there some easy way to tell how big the screen is?
Absolutely! We just need to tell our application how big we WANT it to be!

Hop back over to DesktopLauncher.java and add the following code to set the resolution of our screen.
public static void main (String[] arg) {
Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
config.setForegroundFPS(60);
config.setTitle("Image Editor");
config.setWindowedMode(584, 480);
584 x 480 is the resolution for 480p. We chose this resolution arbitrarily, but a smaller one will be preferred as it will en sure our program
will run on computer monitors of any size, WITHOUT us having to change how anything is ever displayed

What does resolution mean?


Resolution is generally a measure of how DENSE the pixels are displayed on a monitor. The higher the resolution, the more tightly packed the pixels will be, and
the higher the visual quality of our program. For our purposes though, resolution and screen-size will be tied together one-to-one for the sake of simplicity, so
resolution is also going to change the size of our program on our screen as well.

We can now grab the resolution of our application back over in ImageEditor, and save it into a Vector2
public class ImageEditor extends ApplicationAdapter {
SpriteBatch batch;
Rec2D rectangle;
private Vector2 _screenSize;
public void create () {
batch = new SpriteBatch();
_screenSize = new Vector2(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Grabbing the width and height of our application window and saving it

With the size of our screen now stored, we can take a look back at the pseudo-code from earlier
//if(rectangle.position is out of bounds)
// rectangle.velocity.x *= -1;

“Out of bounds” is kind of a vague metric. I’ll replace it with something that will hopefully illuminate why we bothered setting the resolution of our
application and saving it here in ImageEditor.

batch.draw(rectangle.RecTexture, rectangle.Position.x, rectangle.Position.y);


//if(rectangle.position.x is greater than the width of the screen)
// rectangle.velocity.x *= -1;
rectangle.Position.add(rectangle.Velocity);

Go ahead and implement this pseudo-code in your program. Your rectangle should still move to the right, and just when the rectangle is just about
to walk off screen again….

AHA! It should now have bounced off to the left…


….and it’s still going
….and it’s walked off the left-side of the screen and disappeared

Add an extra check to our if-statement to make sure that this doesn’t happen
Hint: Position.x is negative when we start walking off the left-side of the screen
If successful, you should have something like this

Going left, bouncing right, then starting to go left again (Sorry, Word/PDFs don’t like working together with videos)

If you were successful with this, try adding the same checks, but for the y-axis. If you do this right, you should be able to give your rectangle a y-
velocity, and see your rectangle bouncing around the screen diagonally!

Just for fun, let’s add one final piece of functionality to our bouncing rectangle program. Every time we bounce off the sides, let’s change the color
of our rectangle. To do this, all we should need is a new method in Rec2D called changeColor()

We’re at the end now, so I’ll just give this one to you
public void changeColor(Color newColor) {
_recColor = newColor;
//Regernate our texture using our new color
generateTexture();
}

Now, whenever you hit the side of one of the walls, call rectangle.changeColor(). We’ll also generate a new random Color each time this happens.

rectangle.Velocity.x *= -1;
Random random = new Random();
rectangle.changeColor(new Color(random.nextFloat(), random.nextFloat(), random.nextFloat(), 1));
Generating random red, green, and blue values

Why is that last number always 1?


The last number is our color ’s Alpha component. This number rep resents transparency. We don’t want our colors to be see-through, so this
value will always be 1

I’d also recommend changing our screen-wipe color to black for some better immersion

public void render () {


ScreenUtils.clear(0f, 0f, 0f, 1);

1.5 Wrapping Up
Last thing before we go. We’ve made a lot of changes to our program by now
Let ’s save everything we’ve done to Github.

Open up Github Desktop and you should see something like this
This entire time, Git has been tracking the changes we’ve made to our project. Any new files that we’ve added can be seen with a green “+” icon.
Any files that we edited (both added and removed content) are shown with a yellow “.”. Before we upload anything, we can review what we’ve done
to the codebase since the last time we uploaded anything.

In my case, I added two .pngs while I was working on demonstrations for this instruction manual. I don’t actually want to upload those files to my
repository as they were just testing files that I was working with temporarily. I want to remove them now before uploading.

Now, I COULD try and track these files down and delete them manually, if I wanted, but Git makes this a lot easier. I can click on the checkbox to say
that I don’t want to upload it, and the file will remain in the project, but won’t be uploaded.

Or, I could right-click and select Discard changes… and this will delete the files for me.

Now I’ll write a commit message and description of what I’ve done

Write whatever you want here

Now, I’m ready to upload all of my changes. But first, let’s talk very briefly about what Github is actually doing here.

Git Pipeline
In brief, I promise!

• Changes created locally


• We “add” and “remove” any desired files from tracking
o This is what the checkbox in Github Desktop is for
▪ Files are always tracked by default, but we can pick and choose which files we want to eventually package
up
• We “commit” our changes
o A commit happens locally on our machine. It essentially batches all of our files into a single package. Once we
have this package, we can do whatever we want with it, and send it wherever we want
• Finally, we “push” our commit
o This is the step that finally sends our data out to Github
If we want to actually upload our changes, we need to start by first “committing” our changes.
Click Commit to main at the bottom

Our changes have now disappeared

Lastly, click Push origin in the top-left, and our changes will be sent out to Github

CONGRATULATIONS!
The first step is always the most difficult, but you’ve made it! We’ve a long way ahead, but all of you who’ve made it here, you should feel proud of
what you’ve already built.

You might also like