Motivation
Welcome in the training center, cadet, in which during 12 weeks of training we will make you a proper man (in other words object-oriented programmer)! The form and looks of your training will be taken care of by our tactical and strategic team which has long years of experience with training cadets of your calibre and certainly will not let you take a break. Your training is, in fact, its priority.
Today it will be just a warm-up round in which we will see who you are, what's in you and what to expect from you. Even though it is just a warm-up, many cadets break the team and stay on today's mission for few more weeks. We believe you will not be one of them!
From the operation center greets Manager.
Objectives
- Learn to create your own classes, their constructors and methods.
- Learn to overload implementation of a method for various types of parameters.
- Learn to represent the state of an object by instance variables (encapsulation of data).
- Learn to manage visibility of instance variables and methods.
- Learn to create object instances from classes.
Instructions
Step 1: Reactor Class
Your task is to create a class which will represent a reactor in Alien Breed game. Its state will be represented by actual temperature of the core, total damage and animation which visualises reactor's current condition.
Task 1.1
Create a package sk.tuke.kpi.oop.game
in your project.
Since the project uses build tool Gradle, it is necessary to comply with its convention of Java source files placement and therefore you need to create this package in directory src/main/java
.
Task 1.2
In package sk.tuke.kpi.oop.game
create class Reactor
which will be a subclass of AbstractActor
.
In Java, the notation that tells that class A
is a subclass of class B
is the following:
class A extends B {
}
Class AbstractActor
which you will use as a parent class of new class Reactor
is part of library GameLib
which is defined among dependencies of your project. Because of that there is no need to create the class again! You only need to import it into the source file of Reactor
class. The development environment will facilitate the work for you, after you type in a class name that compiler do not recognizes without an import statement
.
Comment
Command import
which is auto-completed by the environment serves to shorten the full name of the class which is sk.tuke.kpi.gamelib.framework.AbstractActor
. After providing the import you can use just the short name AbstractActor
in the file.
Comment
When you need to import some class from provided library and the environment shows several options with the same short class names, you can identify the right choice by package prefix sk.tuke.kpi.gamelib
.
Task 1.3
Add instance variables (also called fields) into class Reactor
which will represent reactor's state: current temperature, damage, and animation visually representing the reactor.
They are the following instance variables:
temperature
- current temperature of reactor represented by whole number, with minimal value 0 degrees,damage
- damage of reactor as percentage, represented by whole numbers in range from 0 (reactor is intact) to 100 (reactor is broken),normalAnimation
- object of animation which visually represents reactor in its intact state. We will use typeAnimation
represented by classAnimation
from packagesk.tuke.kpi.gamelib.graphics
.
When writing instance variables, do not forget that they shouldn't be visible outside the class! Use adequate visibility modifier.
Comment
In computer graphics (mainly in relation to 2D games) a term sprite is used very often. Sprite is a small two-dimensional picture (or animation) which is integrated into larger scene. The effect of animation is created by sequential rendering of several pictures (frames) on the same position.
In game library that we use we will work with objects of type Animation
which can be used equally for simple single images and also images containing several frames of sprite's animation.
Task 1.4
Create a no-argument constructor of class Reactor
in which you initialize instance variables representing state of newly formed object.
In initialization of the object set:
-
animation which will represent the reactor, by using the following image
-
initial temperature of reactor's core to value 0, and
-
initial percentage of reactor's damage to value 0.
Animations are located in the project in directory src/main/resources/sprites/
. When setting path to animation file use a relative path - the root directory is src/main/resources/
. As a result, path to reactor's animation will be sprites/reactor_on.png
.
Comment
When writing path to images with animation you can also use keyboard shortcut Ctrl+Space
to auto-complete names of files and directories.
To set the animation use the following fragment of code in constructor of Reactor
class:
// create animation object
normalAnimation = new Animation("sprites/reactor_on.png", 80, 80, 0.1f, Animation.PlayMode.LOOP_PINGPONG);
// set actor's animation to just created Animation object
setAnimation(normalAnimation);
Comment
A documentation of classes, their constructors and methods can be shown directly in the development environment by placing cursor on the name of desired element and using keyboard shortcut Ctrl+Q
. This shortcut can be used also when going through the list of code auto-complete options (Ctrl+Space
). Documentation is also availablr at a separate page.
Task 1.5
Verify correctness of your implementation by creating an instance of reactor and placing it into the game scene.
If you have proceeded correctly, after you run the project (Shift+F10
) you will be able to use inspector to create instance of class Reactor
and place it into the game.
Task 1.6
Create methods getTemperature()
and getDamage()
which will allow you to obtain current value of reactor's core temperature and damage.
These methods provide access for reading values of temperature and damage. Naturally, their visibility should be set to public
. After that you can check their functionality with inspector.
Comment
Notice that method for obtaining value of instance variable temperature
is called getTemperature()
and for damage
it is called getDamage()
. This naming of methods for obtaining values of variables by adding prefix get
to the name of the variable is conventional and we will use it very often. The resulting method is also colloquially called getter.
Step 2: Intermezzo - Method Overloading
Now when you know how to create class and its methods we will show you one of several properties of polymorphism (we will return to it several times later). The property is method overloading.
Task 2.1
In package sk.tuke.kpi.oop.game
create class Computer
as subclass of AbstractActor
.
To set its animation use image computer.
Task 2.2
In class Computer
create methods for basic arithmetic operations add()
and sub()
, both of them for numeric data types int
and float
.
Each of these methods will have 2 parameters of the same type for operands of given arithmetic operation. In one case parameters will be of type int
, in other case float
. As a result, 4 methods need to be implemented: add()
for int
, add()
for float
, sub()
for int
and sub()
for float
type. By implementing methods in described way they will become overloaded.
Comment
Which of the two implemented methods with name add
(the same for method sub
) will be called is determined statically during compilation based on types of arguments provided in method call.
Verify your implementation by running the project and using inspector.
Step 3: Reactor (Over)Heating
Back to reactor! You already know how to get its state and now we will take a look at how to change the state.
Task 3.1
Create method increaseTemperature()
by using which increasing current temperature of reactor's core will be possible.
This method will return no value and will have one whole-numbered parameter (named e.g. increment) which will represent value by which actual temperature of reactor should increase.
When implementing the method, consider the following conditions:
-
With increasing reactor's temperature linearly increase its damage. Reactor starts to be damaged after exceeding temperature 2000 degrees and completely breaks when reaching 6000 degrees and more. Every following attempt to cool down the reactor will not affect its damage that was caused by high temperature (it means that after calling this method reactor's damage cannot be lower than before the method call). When calculating damage, round the decimal part of the result down.
Comment
Decreasing reactor's damage will be possible only by repairing the reactor, which will be your task later.
-
If the temperature after increasing exceeds value 4000 degrees, from that moment reactor's appearance will be represented by the following animation:
-
If reactor's damage is in the interval <33%, 66%>, temperature rises 1.5-fold; if it exceeds value 66%, temperature rises twofold. In both cases the change of temperature's increase is based on the initial value of parameter increment. After calculating new value of temperature always round the decimal part of the result up to the nearest integer.
-
If temperature after increasing reaches (or exceeds) value 6000 degrees, reactor will be broken (its damage reaches value 100%). Broken reactor will be represented by the following animation:
-
Once reactor's damage reached value 100%, any further increase of temperature will have no effect.
Comment
It's convenient to create objects of animations when initializing the reactor object and storing references to them into another instance variables. Since reactor can be in several states (specifically in 3 states - state ok, overheated, and broken), animation representing reactor's state will change. If reactor changes state during gameplay e.g. 50 times, it means that by each change of animation you need to load the other animation over - in summary 50 loadings of animation files. However, if you load all three animations when initializing the object of reactor, you will perform only 3 animation loadings and the animation objects can be used as needed.
Task 3.2
Create method decreaseTemperature()
which will allow to decrease actual temperature of reactor's core.
This method will return no value and will have one whole-numbered parameter (named e.g. decrement) which will represent value by which actual temperature of reactor should decrease. Beware, decreasing temperature will not decrease the damage which has already been made by high temperature!
When implementing the method, consider the following conditions:
- If damage of reactor's core is at least 50%, actual decreasing of temperature will be halved compared to the value of parameter decrement.
- If damage of reactor's core is at value 100%, decreasing temperature has no effect at all.
- If resulting temperature of reactor's core drops to 4000 degrees or less, change the animation representing state of reactor to reactor_on.
Task 3.3
Create method updateAnimation()
which will set proper animation of reactor according to its current temperature.
When you look carefully at methods increaseTemperature()
and decreaseTemperature()
you will notice that both methods address setting proper animation depending on its temperature. Such duplicity is useless and complicates future changes of functionality. Because of that, refactor your code to separate the common functionality into another method updateAnimation()
.
Task 3.4
Adjust your implementation of methods increaseTemperature()
and decreaseTemperature()
so that they use method updateAnimation()
to properly change current animation of reactor.
After refactoring from previous task do not forget to change also original methods from which the code was extracted so that they will use the new method.
Comment
Method updateAnimation()
represents only an implementation detail and it is not necessary (neither even desirable) to make it accessible to users of reactor's object. Therefore, hide this method by using proper access modifier.
Task 3.5
Use inspector in the game, place reactor's object into the scene and verify correctness of your implementation.
Step 4: Repository
Task 4.1
Upload (commit and push) your source code to your GitLab repository. You should do it before the following exercise. Also, prepare questions that you want to discuss on the following exercise.
Upload your implementation even if it's not yet complete. We recommend to comment out parts of the code that could cause compilation errors.
Additional Tasks
Task A.1
Handle case when methods increaseTemperature()
and decreaseTemperature()
are called with negative value as argument.
If such method call occurs do not change reactor's temperature.
Task A.2
Modify reactor's implementation so that the speed of pulsing created by its animation will depend on reactor's damage.
When reactor's damage increases, duration of one frame of animation should decrease to create the effect of faster pulsation of reactor.
Additional links
- Java Tutorial: Language basics
- Java Tutorial: Classes
- Java Tutorial: Objects
- Java Tutorial: Inheritance