Código básico em Java. O que posso melhorar?

Galera, sou iniciante em java, aprendi recentemente os conceitos de classes abstratas, polimorfismo, etc. Resolvi fazer esse modelo básico de um jogo. Queria saber o que vocês que são mais experientes acham. Estes códigos que fiz estão bem feitos? O que pode ser melhorado? O que está errado? Qualquer dica é bem vinda, pois fiz no intuito de aprender mais. Agradeço desde já quem poder ajudar e peço desculpas pelo tamanho que ficou o tópico.

package model_chars;

import model_equips.AbstractEquipamento;

public abstract class AbstractPersonagem {

    private final String nome;
    private double atack;
    private double def;
    private int hp;
    private int mp;

    public AbstractPersonagem(String nome, int hp, int mp, double atack, double def) {
        this.hp = hp;
        this.mp = mp;
        this.atack = atack;
        this.def = def;
        this.nome = nome;
    }

    public abstract void atacar(AbstractPersonagem p);

    public abstract boolean canUse(AbstractEquipamento equipamento);

    public final double getAtack() {
        return this.atack;
    }

    public final double getDef() {
        return this.def;
    }

    public final int getHp() {
        return this.hp;
    }

    public final int getMp() {
        return this.mp;
    }

    public final void setAtack(double atack) {
        this.atack = atack;
    }

    public final void setDef(double def) {
        this.def = def;
    }

    public final void setHp(int hp) {
        this.hp = hp;
    }

    public final void setMp(int mp) {
        this.mp = mp;
    }

    public final String getNome() {
        return this.nome;
    }
}
package model_chars.jogador;

import model_chars.AbstractPersonagem;
import model_chars.jogador.classes.Guerreiro;
import model_chars.jogador.classes.Mago;
import model_equips.AbstractEquipamento;
import model_equips.arma.AbstractArma;
import model_equips.arma.classes.Faca;
import model_equips.armadura.AbstractArmadura;
import model_equips.armadura.classes.ArmaduraSimples;
import exceptions.LevelLimitException;

public abstract class AbstractJogador extends AbstractPersonagem{
    private static final int max_lv = 30;
    private int level;
    private AbstractArma arma;
    private AbstractArmadura armadura;
    private final Class<AbstractJogador> jogadorClass;
    
    public AbstractJogador(String nome, Class jogadorClass){
    	super(nome, 400, 200, 10, 5);
        this.level = 1;
        this.arma = new Faca();
        this.armadura = new ArmaduraSimples();
        this.jogadorClass = jogadorClass;
    }
    
    public abstract void redefineStatus();
    
    public final void levelUp() throws LevelLimitException{
    	if (this.getLevel() == max_lv){
    		throw new LevelLimitException();
    	}
        this.level++;
        this.redefineStatus();
    }
    
    @Override
    public boolean canUse(AbstractEquipamento equipamento) {
		Class[] restrict_cls = equipamento.getRestrictClass();
		for (Class classe : restrict_cls){
			if (classe.equals(this.getJogadorClass())){
				return true;
			}
		}
		return false;
	}
    
    private Class getJogadorClass() {
		return this.jogadorClass;
	}

    @Override
    public final void atacar(AbstractPersonagem monstro){
    	
    }

    public final int getLevel() {
        return this.level;
    }

    public final AbstractArma getArma(){
    	return this.arma;
    }
    
    public final void setArma(AbstractArma arma){
    	this.arma = arma;
    }
    
    public final AbstractArmadura getArmadura(){
    	return this.armadura;
    }
    
    public final void setArmadura(AbstractArmadura armadura){
    	this.armadura = armadura;
    }
    
    public final double mostraAtaque(){
    	if (this.arma != null){
    		return this.arma.getAtack() + this.getAtack();
    	}
    	return this.getAtack();
    }
    
    public final double mostraDefesa(){
    	if (this.armadura != null){
    		return this.armadura.getDef() + this.getDef();
    	}
    	return this.getDef();
    }
}
package model_chars.jogador.classes;

import model_chars.jogador.AbstractJogador;

public class Guerreiro extends AbstractJogador{

	public Guerreiro(String nome) {
		super(nome, Guerreiro.class);
	}

	@Override
	public void redefineStatus() {
		int lv = this.getLevel();
	    this.setAtack(this.getAtack() + Math.pow(lv, 0.7));
	    this.setDef(this.getDef() + Math.pow(lv, 0.9));
	    this.setHp(this.getHp() + (int)Math.pow(lv, 0.9));
	    this.setMp(this.getMp() + (int)Math.pow(lv, 0.4));
	}
}

[code]
package model_chars.jogador.classes;

import model_chars.jogador.AbstractJogador;

public class Mago extends AbstractJogador{

public Mago(String nome) {
	super(nome, Mago.class);
}

@Override
public void redefineStatus() {
	int lv = this.getLevel();
    this.setAtack(this.getAtack() + Math.pow(lv, 0.9));
    this.setDef(this.getDef() + Math.pow(lv, 0.5));
    this.setHp(this.getHp() + (int)Math.pow(lv, 0.7));
    this.setMp(this.getMp() + (int)Math.pow(lv, 0.8));
}

}[/code]

[code]package model_equips;

public abstract class AbstractEquipamento {
private final String nome;
private final int lv_min;
private final String description;
private final Class[] restrict_cls;

public AbstractEquipamento(String nome, int lv_min, String description, Class[] restrict_cls){
	this.lv_min = lv_min;
	this.description = description;
	this.restrict_cls = restrict_cls;
	this.nome = nome;
}

public final int getLv_min(){
	return this.lv_min;
}

public final String getDescription(){
	return this.description;
}

public final String getNome(){
	return this.nome;
}

public final Class[] getRestrictClass(){
	return this.restrict_cls;
}

}[/code]

[code]
package model_equips.arma;

import model_equips.AbstractEquipamento;

public abstract class AbstractArma extends AbstractEquipamento{
private final double atack;

public AbstractArma(String nome, int lv_min, String description, double atack, Class[] restrict_cls){
	super(nome, lv_min, description, restrict_cls);
	this.atack = atack;
}

public final double getAtack(){
	return this.atack;
}

}[/code]

[code]
package model_equips.armadura;

import model_equips.AbstractEquipamento;

public abstract class AbstractArmadura extends AbstractEquipamento{
private final double def;

public AbstractArmadura(String nome, int lv_min, String description, Class[] restrict_cls, double def) {
	super(nome, lv_min, description, restrict_cls);
	this.def = def;
}

public double getDef(){
	return this.def;
}

}[/code]

[code]
package model_equips.arma.classes;

import model_chars.jogador.classes.Guerreiro;
import model_chars.jogador.classes.Mago;
import model_equips.arma.AbstractArma;

public class Faca extends AbstractArma{
private static final String nome = “Faca”;
private static final String description = “Uma faca simples, não é muito perigosa”;
private static final int lv_min = 1;
private static final Class[] restrict_cls = {Mago.class, Guerreiro.class};
private static final double atack = 10;

public Faca() {
	super(nome, lv_min, description, atack, restrict_cls);
}

}[/code]

[code]
package model_equips.arma.classes;

import model_chars.jogador.classes.Guerreiro;
import model_equips.arma.AbstractArma;

public class Machado extends AbstractArma{
private static final String nome = “Machado”;
private static final String description = “Um machado velho, parece ainda funcionar bem”;
private static final int lv_min = 5;
private static final Class[] restrict_cls = {Guerreiro.class};
private static final double atack = 42.4;

public Machado() {
	super(nome, lv_min, description, atack, restrict_cls);
}

}[/code]

[code]
package model_equips.armadura.classes;

import model_chars.jogador.classes.Guerreiro;
import model_chars.jogador.classes.Mago;
import model_equips.armadura.AbstractArmadura;

public class ArmaduraSimples extends AbstractArmadura{
private static final int lv_min = 1;
private static final String description = “Uma armadura simples feita de couro, não é muito forte”;
private static final int def = 8;
private static final Class[] restrict_cls = {Mago.class, Guerreiro.class};
private static final String nome = “Armadura de couro”;

public ArmaduraSimples() {
	super(nome, lv_min, description, restrict_cls, def);
}

}[/code]

Alguém?

Alguém pode dar uma ajuda? :frowning:

Está ficando legal. Só uma dúvida, para que serve esse atributo?

 private final Class&lt;AbstractJogador&gt; jogadorClass;  

Para determinar a classe de um jogador. Classe nesse caso é nos dois sentidos, tanto do java como uma classe (mago, guerreiro) do jogo. Eu guardo essa informação por causa do método “canUse” que está definido em AbstractJogador.

Não é necessário. Todo objeto já sabe a própria classe. Existe um método chamado getClass que retorna isso. E existe o operador instanceof para testar isso.

Esse método pode ficar assim:

@Override public boolean canUse(AbstractEquipamento equipamento) { Class[] restrict_cls = equipamento.getRestrictClass(); for (Class classe : restrict_cls){ if (classe.equals(this.getClass())){ return true; } } return false; }

Ah beleza! Muito obrigado, já poupa um atributo. Em relação aos modificadores final, está certo utilizar dessa forma? Meu professor de programação disse que se deve utilizar com “moderação”, por que se alguém quiser utilizar meu código futuramente e quiser fazer alguma modificação não precisar ficar alterando isso. Afinal devo levar em conta isso na hora de fazer o código ou devo fazer de acordo com o meu uso? Muito obrigado pela ajuda!

Tem outros detalhes. Lembre-se que a relação herança define a relação É UM. E não TEM UM.

No caso, você está colocando que o Mago é um Jogador. Isso soa estranho, já que os inimigos podem ser magos, não podem?

O problema é que um jogador e um inimigo TEM A profissão de mago. Nesse caso, a hierarquia de classes deveria ser a classe Mago ser filha da classe Profissao. E o jogador ter uma propriedade chamada Profissao (evitei chamar profissão de classe, como no RPG, pois aí ficaria confuso com classe da orientação a objetos).

Agora, como no seu game, não há diferença entre um guerreiro e um mago além dos valores de atributos, eu não criaria subclasses para isso. Você poderia era criar uma outra classe, FabricaPersonagem, com os métodos criarMago(), criarGuerreiro(), etc… e essa classe retornar para você os atributos e multiplicadores (que poderiam ser guardados em variáveis) setados. Se você for implementar mais diferenças do que isso, aí talvez valha a pena manter as subclasses.

Nesse caso está certo sim. Na verdade, deu para ver que você entendeu bem os conceitos de OO (o que é muito bom), agora é só uma questão de absorver com o tempo as boas práticas. Mas isso só se ferrando um bocado mesmo. :slight_smile:

Ah sim, entendi. Nesse caso meu objetivo era que um jogador fosse um mago ou um guerreiro, e os inimigos seriam outra classe chamada monstro que herdariam de AbstractPersonagem. Eu cheguei a criar a classe mas não coloquei aqui porque não implementei ela. Muito obrigado pelas dicas!

A escolha de fazer um jogo foi sua, ou foi o objetivo do professor?

Foi minha escolha mesmo. Sempre gostei da área de programação de jogos, apesar de conhecer pouco ainda.

Legal, tomara que um dia você venha a ser meu aluno na pós! :smiley:

Você trabalha com programação de jogos? Legal! Em java mesmo ou outras linguagens?

Outras linguagens.

Java não presta para jogos (exceto, claro, se você for programar para Android).
Em java eu só faço brincadeiras, como esse ray tracer feito em Java2D: https://github.com/ViniGodoy/raytracer

O game educacional que apresentamos na CEBIT foi em C#. E os que desenvolvi para empresas ou foram em C++, ou foram em Unity. Mas eu mesmo desenvolvo mais ferramentas relacionadas a computação gráfica (otimizador de meshes, cargas de terrenos, geradores de partículas, ferramentas para identificar sinais no Kinect, entre outras coisas). O próximo estamos focando em IOS, com a SpriteKit.

Eu dou aula na especialização e na graduação de desenvolvimento de jogos, aqui na PUCPR, em Curitiba.

Ah legal! Já é um projeto meu aprender C++. Qual você me aconselha a começar (depois de terminar a parte de java da faculdade) ? C# ou C++? Alguma dica de por onde começar? (acabei estendendo bastante o assunto do tópico kkk). Legal esse Ray Tacer, eu tinha visto a imagem que você postou num tópico daqui.

Depende. Você é o tipo de cara que quer fazer um game, ou é mais interessado nos meandros do funcionamento das coisas?

Se quiser fazer um jogo, veja C# e a Unity.

Se quiser estudar a fundo como games funcionam, C++.

No Ponto V, tem um Roadmap de livros para C++:
http://www.pontov.com.br/site/cpp/46-conceitos-basicos/88-roadmap-c

Mas é bom ver também as linguagens que são pedidas nos anuncios de emprego na sua região. É bem mais fácil achar um emprego genérico antes de ir para um nicho específico, como jogos.

Vou dar uma olhada em ambos. Ver o que mais me interessa! Muito obrigado pela ajuda! :slight_smile: