Lets image a simple board game:
Every turn you can place one figure.
You must not remove a figure.
When a certain condition is met, the game is over.
Now I want to implement an undo function.
I have to following classes:
Game {
(...)
+ placeFigure(int,int): void
+ isGameOver(): bool
}
Move {
(...)
+ Move(Game,int,int)
+ execute(): void
+ undo(): void
}
History {
- moves: List<Move>
(...)
+ add(Move): void
+ undo(): void
}
My problem is, in order for a move to be undone, I have to add a function removeFigure.
But, calling this function improperly would result in an invalid state.
The other idea I hade was to make Move a nested class of Game.
This however, will bloat the Game class and make it tedious to add other possible moves in the future.
Should I implement one of those ideas?
Perhaps there is a simpler solution to this problem?
Without knowing the exact objectives of your game nor what other types of moves you had in mind (I assumed moves that also place figures), here's the approach I, in java, would probably take based on the current constraints you've put forth.
/* Game.java */
import java.util.Stack;
public class Game
{
protected static abstract class Move
{
protected final int x;
protected final int y;
protected Move(int x, int y) {
this.x = x;
this.y = y;
}
protected void execute(Game game) {
game.placeFigure(x, y);
}
protected void undo(Game game) {
game.removeFigure(x, y);
}
}
private Stack<Move> history = new Stack<Move>();
public void doMove(Move move) {
move.execute(this);
history.push(move);
}
public void undoMove() {
if(history.empty()) {
System.out.println("no move to undo");
}
else {
Move move = history.pop();
move.undo(this);
}
}
protected void placeFigure(int x, int y) {
System.out.println("place figure at: " + x + "," + y);
}
protected void removeFigure(int x, int y) {
System.out.println("remove figure at: " + x + "," + y);
}
}
/* DefaultMove.java */
public class DefaultMove extends Game.Move
{
public DefaultMove(int x, int y) {
super(x, y);
}
}
/* SpecialMove.java */
public class SpecialMove extends Game.Move
{
public SpecialMove(int x, int y) {
super(x + 10, y + 10);
System.out.println("SpecialMove displaces x and y by 10");
}
}
/* Main.java */
public class Main
{
public static void main(String[] arguments) {
Game game = new Game();
DefaultMove defaultMove = new DefaultMove(3,6);
SpecialMove specialMove = new SpecialMove(4,5);
game.doMove(defaultMove);
game.doMove(specialMove);
game.undoMove();
game.undoMove();
game.undoMove();
}
}
Which prints:
SpecialMove displaces x and y by 10
place figure at: 3,6
place figure at: 14,15
remove figure at: 14,15
remove figure at: 3,6
no move to undo
Maybe it's not at all what you are looking for, but hopefully this gives you some ideas to work with. Let me know if you had completely other objectives in mind and I'll see if I can accommodate.
Related
Trying to make a runner game. I don't want my character to fall off the edge of the map. How do I make the character always center of the path? I want to make so that my character is always center of the path as it is being instantiated and to never fall off the map.
public class GroundTile : MonoBehaviour
{
GroundSpawner groundSpawner;
// Start is called before the first frame update
private void Start()
{
groundSpawner = GameObject.FindObjectOfType<GroundSpawner>();
}
private void OnTriggerExit (Collider other)
{
groundSpawner.SpawnTile();
Destroy(gameObject, 2);
}
// Update is called once per frame
void Update()
{
}
}
public class GroundSpawner : MonoBehaviour
{
public GameObject groundTile;
Vector3 nextSpawnPoint;
// Start is called before the first frame update
public void SpawnTile()
{
GameObject temp = Instantiate(groundTile, nextSpawnPoint, Quaternion.identity);
nextSpawnPoint = temp.transform.GetChild(1).transform.position;
}
private void Start()
{
for (int i = 0; i < 10; i++)
{
SpawnTile();
}
}
}
I tried so many methods.
so I am getting the following error:
Exception in thread "Thread-0" java.lang.NullPointerException
at dev.tamir.firstgame.entities.creatures.Player.getInput(Player.java:19)
at dev.tamir.firstgame.entities.creatures.Player.tick(Player.java:31)
at dev.tamir.firstgame.states.GameState.tick(GameState.java:25)
at dev.tamir.firstgame.Game.tick(Game.java:65)
at dev.tamir.firstgame.Game.run(Game.java:110)
at java.lang.Thread.run(Unknown Source)
And I've checked all the lines Java had marked me and I can not find what is producing the null.
Player:
package dev.tamir.firstgame.entities.creatures;
import java.awt.Graphics;
import dev.tamir.firstgame.Game;
import dev.tamir.firstgame.gfx.Assets;
public class Player extends Creature {
private Game game;
public Player(Game game, float x, float y) {
super(game, x, y, Creature.DEFAULT_CREATURE_WIDTH, Creature.DEFAULT_CREATURE_HEIGHT);
}
#Override
public void tick() {
getInput();
move();
game.getGameCamera().centerOnEntity(this);
}
private void getInput() {
xMove = 0;
yMove = 0;
if(game.getKeyManager().up)
yMove = -speed;
if(game.getKeyManager().down)
yMove = speed;
if(game.getKeyManager().left)
xMove = -speed;
if(game.getKeyManager().right)
xMove = speed;
}
#Override
public void render(Graphics g) {
g.drawImage(Assets.robro[7], (int) (x - game.getGameCamera().getxOffset()), (int) (y - game.getGameCamera().getyOffset()), width, height, null);
}
}
Gamestate:
package dev.tamir.firstgame.states;
import java.awt.Graphics;
import dev.tamir.firstgame.Game;
import dev.tamir.firstgame.entities.creatures.Player;
import dev.tamir.firstgame.tiles.Tile;
import dev.tamir.firstgame.worlds.World;
public class GameState extends State {
private Player player;
private World world;
public GameState(Game game) {
super(game);
player = new Player(game, 0, 0);
world = new World(game, "res/worlds/world1.txt");
}
#Override
public void tick() {
world.tick();
player.tick();
}
#Override
public void render(Graphics g) {
world.render(g);
player.render(g);
}
}
Game:
package dev.tamir.firstgame;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import dev.tamir.firstgame.Display.Display;
import dev.tamir.firstgame.gfx.Assets;
import dev.tamir.firstgame.gfx.GameCamera;
import dev.tamir.firstgame.input.KeyManager;
import dev.tamir.firstgame.states.GameState;
import dev.tamir.firstgame.states.MenuState;
import dev.tamir.firstgame.states.State;
public class Game implements Runnable {
private Display display;
private Thread thread;
private BufferStrategy bs;
private Graphics g;
//States
private State gameState;
private State menuState;
//Input
private KeyManager keyManager;
//Camera
private GameCamera gameCamera;
private boolean running = false;
private int width, height;
public String title;
public Game(String title, int width, int height) {
this.width = width;
this.height = height;
this.title = title;
keyManager = new KeyManager();
}
private void init() {
display = new Display(title, width, height);
display.getFrame().addKeyListener(keyManager);
Assets.init();
gameCamera = new GameCamera(this, 0,0);
gameState = new GameState(this);
menuState = new MenuState(this);
State.setState(gameState);
}
private void tick() {
keyManager.tick();
if(State.getState() != null)
State.getState().tick();
}
private void render() {
bs = display.getCanvas().getBufferStrategy();
if(bs == null) {
display.getCanvas().createBufferStrategy(3);
return;
}
g = bs.getDrawGraphics();
//Clear
g.clearRect(0, 0, width, height);
//Draw
if(State.getState() != null)
State.getState().render(g);
//End of Draw
bs.show();
g.dispose();
}
public void run() {
init();
int fps = 60;
double timePerTick = 1000000000 / fps;
double delta = 0;
long now;
long lastTime = System.nanoTime();
long timer = 0;
int ticks = 0;
while (running) {
now = System.nanoTime();
delta += (now - lastTime) / timePerTick;
timer += now - lastTime;
lastTime = now;
if(delta >= 1) {
tick();
render();
ticks++;
delta--;
}
if(timer >= 1000000000) {
System.out.println("FPS: " + ticks );
ticks = 0;
timer = 0;
}
}
stop();
}
public KeyManager getKeyManager() {
return keyManager;
}
public GameCamera getGameCamera() {
return gameCamera;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public synchronized void start() {
if (running)
return;
running = true;
thread = new Thread(this);
thread.start();
}
public synchronized void stop() {
if (!running)
return;
running = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
help would be very appreciated, as I've been looking for hours now and I don't know what is causing the null and I am suspecting the error log may even be misleading me.
My guess would be you have a private Game in your creature class. When you call super() in Player, you pass a Game object to Creature. The Creature constructor most likely has a line like this.game = game;
Of course, I can't say for sure because the Creature class is not included in your post, but that's the most likely code setup. Because game would then be private to Creature, Player cannot see it. That means the private Game game that you declare in Player is never set.
After you call super, do this.game = game;
This will almost certainly take care of your issue.
Just for future reference, the message you got when the error resulted is the call stack; basically it tells you what methods called what, the most recent being at the top. The error took place at line 19 in Player.getInput(), which was called by tick() in that same class.
The only object you use in getInput() is game, and so that must be the source of the null pointer. From there, it's a quick check to see that game is a private field of Player, and since it is null that's a huge clue that it was never initialized (though that is not always the case). Private fields most often are initialized in the class constuctor (but they don't have to be... your Player class is pretty sparse so it wouldn't take that long to look through all of it if you absolutly had to. Looking at the Player constructor, we see a Game object named game is passed in, which suggests that it was intended to be used to initialize game and yet never is. VoilĂ , we found the problem!
I'm sorry if that last paragraph felt a little condecending; it wasn't meant to be. I just wanted to walk you through how I found your issue. Hopefully knowing how I found it will help you find later errors on your own.
I'm not really sure how I would phrase the title right, so I apologize for the initial confusion.
This is just a small question I had about how to structure code and such and I have no idea on what to call it so I will explain it with this example:
Say I am writing a Call of Duty type game where the player can customize their weapons with certain attachment.
I have a class that defines each gun. It looks something like this:
class Gun {
int clip = 30;
int ammo = 100;
float reloadTime = 5f;
float damage = 10f;
Attachment[] attachments;
//Plus some not included attachments.
void shoot() {
//...
}
void reload() {
//...
}
void applyAllAttachments() {
//Apply the list of attachments' effects
}
}
class Attachment {
void effect() {
//change the gun in some way.
}
}
Now I would like to add 4 attachments, Fast Mags (increase reload speed), Hollow Point (increase damage), Grenade Launcher (Secondary Gun) and Minigun (Replace the barrel with a minigun or something).
For the Fast Mags and the Hollow Point, it should be simple, all I have to do is change a number or a value, but for the Grenade Launcher and Minigun, which have custom, extra functions (like Unity Delegates), would it be wiser to add a function that handles external custom firing types, or would it be better to just have separate methods inside the Gun class that specifically handle to extra minigun functions?
TL;DR
If I want to add a grenade launcher attachment to a gun, should I do this:
class Gun {
int clip = 30;
int ammo = 100;
float reloadTime = 5f;
float damage = 10f;
Attachment[] attachments = Attachment[10];
//Plus some not included attachments.
void shoot() {
//...
customShoot();
}
void customShoot() {
//Apply attachments custom attachment shoot methods.
}
void reload() {
//...
}
void applyAllAttachments() {
//Apply the list of attachments' effects
}
}
class GrenadeLauncher extends Attachment {
#Override
public void effect() {
//Spawn new grenade
}
}
Or This:
class Gun {
int clip = 30;
int ammo = 100;
float reloadTime = 5f;
float damage = 10f;
Attachment[] attachments = Attachment[10];
//Plus some not included attachments.
void shoot() {
//...
if (attachments.GetType() == GrenadeLauncher) {
grenadeLauncherShoot();
}
}
void grenadeLauncherShoot() {
}
void reload() {
//...
}
void applyAllAttachments() {
//Apply the list of attachments' effects
}
}
Sorry for my pseudo/java code, hope it's comprehensible.
The first way is better: You can create new attachments without having to modify the Gun class.
In a general manner, you shouldn't need to check for type, and your code will be cleaner if you don't.
Here, your Attachment class should be abstract (I suppose it already is), and force children to implements some functions.
public abstract class Attachment
{
protected abstract void shoot();
}
Then the gun calls it for all Attachements:
class Gun {
int clip = 30;
int ammo = 100;
float reloadTime = 5f;
float damage = 10f;
Attachment[] attachments = Attachment[10];
//Plus some not included attachments.
void shoot() {
//...
for(int i = 0; i < attachments.length(); ++i) {
attachments[i].shoot();
}
}
void reload() {
//...
}
}
class GrenadeLauncher extends Attachment {
#Override
public void shoot()
{
//Spawn new grenade
}
}
By the way, why did you tag java and Unity? If you work with unity your code should be c# or javascript
For example if I have 3 classes,
class A {
public void doA() {
/* do something */
}
}
class B {
public void doB() {
A a = new A();
a.doA();
}
}
class MyClass {
public static void main(String args[]) {
B b = new B();
b.doB();
}
}
Now I want to define a point cut for flow doB() -> doA(), like if doB() calls doA() grab parameters from class A and class B and do something in aspect method. Could someone help me out.
Let me slightly extend your sample code in order to make you understand what my solution does and what it cannot do:
class A {
public void doA() {}
}
class B {
public void doB() {
new A().doA();
new C().doC();
}
}
class C {
public void doC() {
new A().doA();
}
}
class MyClass {
public static void main(String args[]) {
new A().doA(); // should not be captured
new B().doB(); // should be captured
}
}
As you can see, there is a new class C now and we have three control flows now:
MyClass.main -> A.doA
MyClass.main -> B.doB -> A.doA
MyClass.main -> B.doB -> C.doC -> A.doA
You want to exclude #1 and capture #2, but what about #3? In this case a.doA is called indirectly from B.doB via C.doC. My solution also captures this indirect case. If this is fine for you or it does not happen in your code base, you can use my solution. Otherwise things would get a little more complicated and you would need to inspect the call stack. Tell me if you need to exclude #2, and I will extend my answer, but the solution will not look as simple as this one, I can promise.
Now here is the aspect:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
public class ControlFlowInterceptor {
#Before("execution(void A.doA()) && target(a) && cflow(execution(void B.doB()) && target(b))")
public void advice(JoinPoint thisJoinPoint, A a, B b) {
System.out.println(thisJoinPoint);
System.out.println(" " + a);
System.out.println(" " + b);
}
}
The console output looks like this:
execution(void A.doA())
A#7b19f779
B#65c66812
execution(void A.doA())
A#4df2868
B#65c66812
Please note that we have the same B object ID in both outputs, but because C.doC creates an new A object, we have two different A object IDs.
When I press a direction key to move the object in that direction, it moves once, pauses momentarily, then moves again. Kind of like how if I want to type "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", I would hold "a" key down, but after the first "a" there is a pause then the rest of the "a"'s are typed. How do I remove that pause in KeyListener? Thank you.
This is the key repetition feature that the OS provides, so there is no way around the pauses.
The way most games gets around this is to keep an array of the current state of all required keys and check periodically on them (for example in the game loop) and act on that (e.g move).
public class KTest extends JFrame implements KeyListener {
private boolean[] keyState = new boolean[256];
public static void main(String[] args) {
new KeyTest();
int xVelocity = 0;
int x = 0;
while(1) {
xVelocity = 0;
if(keyState[KeyEvent.VK_LEFT]) {
xVelocity = -5;
}
x += xVelocity;
}
}
KTest() {
this.addKeyListener(this);
}
void keyPressed(KeyEvent e) {
key_state[e.getKeyCode()] = true;
}
void keyReleased(KeyEvent e) {
key_state[e.getKeyCode()] = false;
}
}
Base class taken from: http://content.gpwiki.org/index.php/Java:Tutorials:Key_States