Como rotacionar uma imagem

Eu já até pensei em pegar algum jogo simples para poder ver isso, mas primeiro que não faço idéia de como é a organização dos códigos, então eu fico meio perdido onde devo ir pra procurar o que eu quero, qual arquivo e tals e segundo que mesmo que eu encontre, vai ter muitos recursos que eu não conheço sendo usado, então eu continuaria perdido na hora de aprender. Prefiro deixar pra fazer isso quando tiver mais conhecimento dos outros recursos. Mas obg pela sugestão.

Tipo, eu não coloquei isso e continuou funcionando, porém tem um problema que eu não sei se esse é o motivo, toda vez que o cursor do mouse passa da coordenada do X do centro da imagem, ele retorna pra posição inicial, ele não continua girando para o outro lado.

Em relação ao JavaFX, eu já ouvi falar mas eu queria aprender primeiro o Java bem, saber como os recursos trabalham e agem no código, entender como deve ser feito a implementação de cada classe, como devo organizar o código para funcionar bem, para ai sim ir pro JavaFX, que acredito eu ser mais facil de programar, porém eu não fico muito confortável em usar algo que facilite pra min, sem antes eu entender bem como funciona. Eu pretendo depois ir pro JavaFX, pois me deixou bem interessado mas ainda não :slight_smile: .

O JavaFX já faz parte do Java e não precisa baixar.

Deve ser um problema no cálculo. O que eu postei antes funciona para uma imagem quadrada.

Eu to usando uma imagem quadrada, 100x100.

Acho que já descobrir o problema, na verdade é a condição if que ta errada, pois não é quando a variação de x é negativa mas sim quando a variação de y é negativa

mas agr ele ta girando todo errado, a cada um quarto q mouse percorre ele muda de direção

acho que entendi, eu fiz alguns testes e tipo:

se eu deixo da forma como está o código, toda vez que a variacao de X chega a 0 ele muda pra yc - y porém isso faz com que os valores se tornem igual ao y - yc só que começando pelo lado negativo, então tem uma mudança muito repentina no angulo, então ele espelha a imagem.

se eu deixo apenas fazendo os calculos com y - yc ele some toda vez que o valor do arco seno passa de 1,5 se não me engano e volta quando o valor é igual a -1,5 ou menor

se eu faço y + yc quando x passa a ser menor ou igual a zero, então ele para de girar no sentido do mous pra girar pro outro lado

complicado isso, como é q funciona essa parada de rotate da classe Graphics2D?

Consegui corrigir, vou postar aqui somente parte que mudei:

Obs.: criei uma variavel chamada anguloBase que possui o vlaor de pi/2, pois esse é o valor limite do método Math.asin(), nele só podem ser inseridos valores de -pi/2 até pi/2.

public void girarImagem(int x, int y) {
    definirAnguloBase();
    
    double xc, yc, diferenca;

    xc = imagem.getLocationOnScreen().getX() + (imagem.getWidth())/(2.0);
    yc = imagem.getLocationOnScreen().getY() + (imagem.getHeight())/(2.0);

    double d = Point.distance(xc, yc, x, y);
    double angulo = Math.asin((y-yc)/d);
    
    if(x - xc <= 0) {
        if(angulo > 0) {
            diferenca = anguloBase - angulo;
            angulo = anguloBase + diferenca;
        } else if(angulo < 0) {
            diferenca = anguloBase - angulo;
            angulo = anguloBase + diferenca;
        }
    }

    imagem.setAngulo(angulo);
}

Desculpa voltar aqui, mas acabei de me depara com essa duvida, mas esses dois valores depois de angulo? eles representam oq?

É a origem da transformação, se mudar para:

g2d.rotate(angulo, espaco, espaco);

não irá girar no centro, faça o teste.

Entendi agr.
Eu testei com uma imagem que não tenha todos os lados iguais, mas ele parece que mudou um pouco a posição, então como eu poderia confirmar a área que ele ocupa? pois se eu quiser fazer dois objetos se colidirem, a imagem deles vão acabar se sobrepondo uma a outra? ou nada disso interfere?

Calculando, geralmente criasse uma representação em forma de retângulo ou circulo.

Sim, por isso que se usa motor de game (“game engine”), é muito complicado esses cálculos. OS mais simples são quando os objetos são círculos e retangulares, mas sem girar.

Se quiser dar continuidade a isso, recomendo que busque um motor de jogo. Vc poderia o JavaFX, facilitaria bastante, mas não tem cálculos prontos de colisão.

Eu troquei só a imagem pra fazer alguns testes e pra min a imagem ficou muito distante das bordas, como poderia mover a imagem dentro do JComponent

A imagem ta logo a baixo:

É meio complicado de resolver esse problema, então vou ser um pouco chato e pedir para refazer, também tem um “bug” que não descobri como arrumar, por isso é importante refazer:

Classe Canvas, componente responsável por desenhar, deve ser exatamente:

public class Canvas extends JComponent implements MouseListener, MouseMotionListener, MouseWheelListener {

    private final List<Entity> entities = new ArrayList<>();

    public Canvas() {
        // prepara o fundo do componente
        setOpaque(true);
        setBackground(Color.BLACK);

        // configura os listeners
        addMouseListener(this);
        addMouseMotionListener(this);
        addMouseWheelListener(this);
    }

    @Override
    public void paint(Graphics g) {
        if (g != null) {
            try {
                // limpa o componente
                Graphics2D g2d = (Graphics2D) g;
                g2d.setBackground(getBackground());
                g2d.clearRect(0, 0, getWidth(), getHeight());

                // desenha
                draw(g2d);
            } finally {
                g.dispose();
            }
        }

    }

    public void draw(Graphics2D g) {
        for (Entity e : entities) {
            Graphics2D g2 = (Graphics2D) g.create();
            e.draw(g2);
            g2.dispose();
        }
    }

    public void addEntity(Entity e) {
        entities.add(e);
    }

    public void removeEntity(Entity e) {
        entities.remove(e);
    }

    @Override
    public void mouseDragged(MouseEvent e) {
    }

    @Override
    public void mouseMoved(MouseEvent e) {
    }

    @Override
    public void mouseWheelMoved(MouseWheelEvent e) {
    }

    @Override
    public void mouseClicked(MouseEvent e) {
    }

    @Override
    public void mousePressed(MouseEvent e) {
    }

    @Override
    public void mouseReleased(MouseEvent e) {
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }
}

Interface Entity, objeto que é desenhado no Canvas, deve ser exatamente (pode conter outros métodos se quiser):

public interface Entity {

    public void draw(Graphics2D g);

}

Classe App, geralmente tem o nome do programa, não precisa ser exatamente assim, este é só exemplo:

public class App extends Canvas {

    Personagem personagem;

    public App() {
        super(); // inicializa o canvas
        personagem = new Personagem();
        addEntity(personagem);
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        int mouse_x = e.getX();
        int mouse_y = e.getY();
        int personagem_x = personagem.position.x;
        int personagem_y = personagem.position.y;

        double d = Point.distance(personagem_x, personagem_y, mouse_x, mouse_y);
        if (mouse_x - personagem_x > 0) {
            personagem.angulo = Math.asin((mouse_y - personagem_y) / d);
        } else {
            personagem.angulo = Math.asin((personagem_y - mouse_y) / d);
        }
    }

    @Override
    public void mousePressed(MouseEvent e) { // método que testa movimento
        personagem.position.x = e.getX();
        personagem.position.y = e.getY();
        repaint(); // atualiza
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setSize(500, 400);
        frame.getContentPane().add(new App());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

}

Classe Personagem, também para teste:

public class Personagem implements Entity {

    BufferedImage image;
    Point position = new Point(50, 50); // precisa mudar aqui, a posição será o centro da imagem, que será 100 x 100
    double angulo;

    public Personagem() {
        //InputStream stream = getClass().getResourceAsStream(diretorio);
        //image = ImageIO.read(stream);

        image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);

        // cria um retângulo, somente para teste
        Graphics2D g2d = image.createGraphics();
        g2d.setColor(Color.WHITE);
        g2d.fillRect(0, 0, 100, 100);
        g2d.setColor(Color.RED);
        g2d.fillRect(4, 4, 100 - 8, 100 - 8);
        g2d.dispose();
    }

    @Override
    public void draw(Graphics2D g) {
        g.rotate(angulo, position.x, position.y);
        g.drawImage(image, position.x - image.getWidth() / 2, position.y - image.getHeight() / 2, null);
    }

}

Cada elemento deve implementar a interface Entity que deve ser adicionada no Canvas para ser desenhado, o Canvas também tem o método removeEntity para remover os elementos que não precisam ser mais desenhados.

Eu entendo, vou pegar o código que vc escreveu e vou estudar em cima dele, mas da uma olhada, eu reescrevir o código do Imagem2d que tava antes, na verdade só mudei alguns valores e acrescentei 2 variaveis x e y pra adaptar melhor esse valores, por enquanto eu consegui resolver o que eu queria, agr só falta eu entender melhor como que essas variaveis interferem na imagem, pra eu poder adaptar e implementar um método que vai corrigir os problemas com a imagem ficar cortada, que eu acho que vai ser um pouco simples, pois é só um pouco de matemática e matemática pra min não tanto problema assim

public class Imagens2D extends JComponent {
    int espaco;
    BufferedImage image;
    double angulo;
    int x,y;
    
    public Imagens2D(String diretorio) throws IOException {
        InputStream stream = getClass().getResourceAsStream(diretorio);
        image = ImageIO.read(stream);
        espaco = (int) (Math.max(image.getWidth(), image.getHeight())*Math.pow(2,0.5)/2);
        //mudei a dimensão para o tamanho da imagem
        Dimension dimensoes = new Dimension(image.getWidth(), image.getHeight());
        setPreferredSize(dimensoes);
        setMinimumSize(dimensoes);
        setSize(dimensoes);
        x = 0;
        y = 0;
    }
    
    @Override
    public void paintComponent(Graphics g) {
        if(g != null) {
            Graphics2D g2d = (Graphics2D)g;
             //coloquei o centro de rotação no centro da imagem;
            g2d.rotate(angulo,image.getWidth()/2,image.getHeight()/2);
            //desenha a imagem a partir do ponto 0,0 dentro do JComponent, o ponto 0,0 é relativo ao JComponent
            g2d.drawImage(image,0,0,null);
        }
    }
    //um método pra sempre que eu precisar atualizar o desenho sem precisar mudar outras coisa, ainda ou acrescentar mais algumas formas para cada ação nescessária
    public void atualizarImagem() {
        //repaint();
        //outra forma de usar o mesmo metodo que encontrei e que se adapta melhor pras mudanças
        repaint(0, 0, image.getWidth(),image.getHeight());
    }
    public void setAngulo(double angulo) {
        this.angulo = angulo;
        atualizarImagem();
    }
}

Da forma como está não seria necessário o x,y, usaria o setBounds ou setLocation do componente.

O que eu refiz lá corrige vários problemas, o da imagem cortada, não precisar mais da variável “espaço” que dificulta os cálculos, sem o espaço o calculo da rotação será mais precisa, eventos do mouse podia causar erros quando o evento acontecer antes do componente estar pronto, não será mais necessário usar o x,y relativos no Screen que facilitam os cálculos, imagens sobrepostas podiam cortar outras imagem que estivessem atrás, …

Agora para mover, basta fazer como no seguinte trecho:

public void mousePressed(MouseEvent e) { // método que testa movimento
    personagem.position.x = e.getX();
    personagem.position.y = e.getY();
    repaint(); // atualiza
}

Neste caso, quando clicar irá mover o “personagem”.

Outra coisa, não consegui alterar lá:

Point position;
double angulo;

public Personagem() {
    //InputStream stream = getClass().getResourceAsStream(diretorio);
    //image = ImageIO.read(stream);

    image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
    position = new Point(image.getWidth()/2, image.getHeight()/2);

Eu vou escrever os dois e vou comparar os dois. Eu preciso entender bem o código ainda, pq por enquanto eu entendo o que vc ta falando, mas eu ainda não sei utilizar o código ainda, então eu o pega o código e estudar bem como funciona cada classe, método que vc usa, como eu vou usar. Obg por me ajudar :), to conseguindo entender bem como funciona a parte de Graphics, BufferedImage e sobre JComponent, que eu ainda tenho que me aprofundar, no seu código vc já adiantou uma questão que eu tava começando a querer entender é sobre tipo, eu tenho varias coisa para desenhar, player, itens, inimigos e por ai vai, todos eles tem a sua imagem, posicão e tals, ai que vem problema, na hora organizar o código, eu gostaria de uma classe que fosse responsável por desenhar a imagem, outra por conter as informações da imagem e outra seria a entidade (jogador, iten, inimigo), a minha idéia era que tudo que precisa-se ser desenhado, passaria pela classe responsável por desenhar, sendo que só seria inserido as informações nescessária e o problema é sobre o JComponent, que é ele que é adicionado no JFrame(mais precisamente no JPane dele), eu tava meio confuso em como organizar o código, quem iria se extender ao JComponent, quem iria chamar quem no JFrame.

Primeiro esse g.dispose faz oq?
Segundo eu até hj não consgui entender o sobre o List, eu queria muito sabe o que faz, como eu uso, e geralmente como ele é usado.

List<Entity> entities = new ArrayList<>();

Libera os recursos do graphics. O graphics guarda muitas informações e isso pode deixar o programa lento, então quando não precisar mais, use o dispose.

Tem muita informação sobre o List e ArrayList na web, basta pesquisar. Tem varias tipos de List, o ArrayList usa uma estrutura de array (vetor) que permite adicionar ou remover elementos sem um tamanho definido, portanto pode-se dizer que o ArrayList é um Array com mais recursos. O outro tipo de List também muito usado é o LinkedList, que usa a estrutura de lista ligada ou encadeada.

O “<Entity>” é chamado de “generics” que diz o tipo dos elementos da lista, exemplo o array Integer[] tem elementos do tipo Integer, o List<Integer> tem os elementos também do tipo Integer, o “<>” no ArrayList é equivalente a “<Entity>”.

Acho que entendi um pouco melhor, vou procurar me aprofundar mais, mas antes quero resolver bem essa parte de imagens

Me deparei com um problema que não tinha visto antes ou ele não tinha esse problema e agora tem, mas como pode ver na imagem a baixo, o ponto I é o centro em que a imagem vai girar, até ai ta funcionando certinho, ele gira entorno desse ponto, porém o movimento de girar em relação o mouse não é referente ao ponto I mas sim ao ponto II, eu não consigo ver onde está o problema, mas acredito que seja a questão da posição do mouse ser totalmente diferente em relação ao JFrame, eu ja tive um problema com isso antes, e o que eu fiz foi descobrir essa diferença na tentativa e erro, mas eu gostaria de saber se tem um jeito melhor de resolver isso.

Eu fui na tentativa e erro e encontrei aproximadamente o quanto que tenho de retirar do ponto, pior que foi diferente da outra vez que tive esse problema, eu tava usando o setlocation, e lá eu adicionava +8 no x e + 31 no y de toda imagem que eu colocava, nesse eu tiver que tirar 62 no x e 39 do y nessa parte:

public void girarImagem(double x, double y) {
    definirAnguloBase();
    
    double xc, yc, diferenca;
    xc = imagem.getX() - 62 + (imagem.getWidth())/(2.0);
    yc = imagem.getY() - 39 + (imagem.getHeight())/(2.0);
    double d = Point.distance(xc, yc, x, y);
    if(d == 0) {
        System.out.println("X: " + x + "Y: " + y);
    }
    double angulo = Math.asin((y-yc)/d);
    
    if(x - xc <= 0) {
        if(angulo > 0) {
            diferenca = anguloBase - angulo;
            angulo = anguloBase + diferenca;
        } else {
            diferenca = anguloBase - angulo;
            angulo = anguloBase + diferenca;
        }
    }
    imagem.setAngulo(angulo);
}

Foi feito assim antes. A posição do mouse é relativo ao Screen então a posição a posição da imagem também precisa ser relativo ao Screen, o getX/getY da Imagem é relativa ao contentPane do JFrame e o getX/getY do mouse eu não sei, mas acho que é relativo ao componente em que o ponteiro está em cima, que pode ser o contentPane ou da Imagem.