我正在嘗試制作將移動到螢屏右側的紅色橢圓形影片。但它只是繪制橢圓形。我不知道我做錯了什么,我真的找不到任何關于如何做到這一點的資訊。任何幫助都會很棒,謝謝。
import java.awt.*;
public class mainClass
{
public mainClass()
{
Frame f = new Frame("Canvas Example");
f.add(new MyCanvas());
f.setLayout(null);
f.setSize(400, 400);
f.setVisible(true);
}
public static void main(String args[])
{
new mainClass();
}
}
class MyCanvas extends Canvas
{
int x = 75;
public MyCanvas() {
setBackground (Color.BLACK);
setSize(400, 400);
}
public void paint(Graphics g)
{
g.setColor(Color.red);
g.fillOval(x, 75, 150, 75);
}
public void update(Graphics g)
{
x ;
}
}
uj5u.com熱心網友回復:
理論
影片很難,我的意思是,真正好的影片很難。沒有任何理論可以用來創造好的影片,比如緩和、預期、擠壓……我可以繼續,但我自己很無聊。
關鍵是,簡單地增加一個值(也稱為線性級數)是一種糟糕的影片方法。如果系統很慢、很忙或由于其他原因沒有跟上,影片會因為它(口吃、暫停等)而受到影響。
“更好”的解決方案是使用基于時間的行程。也就是說,您指定從當前狀態移動到新狀態所需的時間以及持續回圈和更新狀態直到時間用完為止。
“主回圈”
如果您對游戲開發進行任何研究,他們總是會談論這個稱為“主回圈”的東西。
“主回圈”負責更新游戲狀態和調度繪制通道。
就您的問題而言,您需要一個“主回圈”,它可以更新橢圓的位置,直到它到達目標位置。
因為大多數 GUI 框架已經在它們自己的執行緒背景關系中運行,所以您需要在另一個執行緒中設定“主回圈”
AWT
一些理論
AWT 是原始的 GUI 框架,所以它是“舊的”。雖然 Swing 確實位于它之上,但您會發現更多的人有 Swing 的經驗,而不是 AWT。
要記住的重要事情之一是,Canvas它不是雙緩沖的,因此,如果您更新組件的速度足夠快,它會閃爍。
為了克服這個問題,您需要實作某種雙緩沖作業流程。
可運行示例
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.time.Duration;
import java.time.Instant;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
Frame frame = new Frame();
frame.add(new TestCanvas());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Ticker implements Runnable {
public interface Callbck {
public void didTick(Ticker ticker);
}
private boolean isRunning = false;
private Thread thread;
private Callbck callback;
public void setCallback(Callbck tick) {
this.callback = tick;
}
public void start() {
if (isRunning) {
return;
}
isRunning = true;
thread = new Thread(this);
thread.setDaemon(false);
thread.start();
}
public void stop() {
if (!isRunning) {
return;
}
isRunning = false;
thread.interrupt();
thread = null;
}
@Override
public void run() {
while (isRunning) {
try {
Thread.sleep(5);
if (callback != null) {
callback.didTick(this);
}
} catch (InterruptedException ex) {
isRunning = false;
}
}
}
}
public class TestCanvas extends Canvas {
private BufferedImage buffer;
int posX;
private Ticker ticker;
private Instant startedAt;
private Duration duration = Duration.ofSeconds(5);
public TestCanvas() {
ticker = new Ticker();
ticker.setCallback(new Ticker.Callbck() {
@Override
public void didTick(Ticker ticker) {
if (startedAt == null) {
startedAt = Instant.now();
}
Duration runtime = Duration.between(startedAt, Instant.now());
double progress = runtime.toMillis() / (double)duration.toMillis();
if (progress >= 1.0) {
stopAnimation();
}
posX = (int)(getWidth() * progress);
repaint();
}
});
}
protected void startAnimtion() {
ticker.start();
}
protected void stopAnimation() {
ticker.stop();
}
@Override
public void setBounds(int x, int y, int width, int height) {
buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
super.setBounds(x, y, width, height);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
public void addNotify() {
super.addNotify();
startAnimtion();
}
@Override
public void removeNotify() {
super.removeNotify();
buffer = null;
}
@Override
public void paint(Graphics g) {
super.paint(g);
if (buffer == null) {
return;
}
Graphics2D g2d = buffer.createGraphics();
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.setColor(Color.RED);
int midY = getHeight() / 2;
g2d.fillOval(posX, midY - 5, 10, 10);
g2d.dispose();
g.drawImage(buffer, 0, 0, this);
}
}
}
什么是Canvas去...?
在大多數情況下,Canvas出于上述許多原因,您應該避免使用,但您可能考慮使用的原因之一Canvas是如果您想完全控制繪畫程序。如果您想要創建一個復雜的游戲并且想要從渲染管道中獲得最佳性能,您可以這樣做。
有關更多詳細資訊,請參閱BufferStrategy 和 BufferCapabilities以及JavaDocs
基于 Swing 的實作
希望我已經讓您相信 Swing 實作可能是更好的解決方案,在這種情況下,您應該使用 SwingTimer而不是Thread,因為 Swing 不是執行緒安全的
有關更多詳細資訊,請參閱Swing 中的并發性和如何使用 Swing 計時器
可運行示例
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.Instant;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Ticker {
public interface Callbck {
public void didTick(Ticker ticker);
}
private Timer timer;
private Callbck callback;
public void setCallback(Callbck tick) {
this.callback = tick;
}
public void start() {
if (timer != null) {
return;
}
timer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (callback == null) {
return;
}
callback.didTick(Ticker.this);
}
});
timer.start();
}
public void stop() {
if (timer == null) {
return;
}
timer.stop();
timer = null;
}
}
public class TestPane extends JPanel {
int posX;
private Ticker ticker;
private Instant startedAt;
private Duration duration = Duration.ofSeconds(5);
public TestPane() {
ticker = new Ticker();
ticker.setCallback(new Ticker.Callbck() {
@Override
public void didTick(Ticker ticker) {
if (startedAt == null) {
startedAt = Instant.now();
}
Duration runtime = Duration.between(startedAt, Instant.now());
double progress = runtime.toMillis() / (double) duration.toMillis();
if (progress >= 1.0) {
stopAnimation();
}
posX = (int) (getWidth() * progress);
repaint();
}
});
}
protected void startAnimtion() {
ticker.start();
}
protected void stopAnimation() {
ticker.stop();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
public void addNotify() {
super.addNotify();
startAnimtion();
}
@Override
public void removeNotify() {
super.removeNotify();
stopAnimation();
}
@Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
int midY = getHeight() / 2;
g2d.fillOval(posX, midY - 5, 10, 10);
g2d.dispose();
}
}
}
uj5u.com熱心網友回復:
這不影片的原因是沒有任何東西觸發組件更新和重新繪制自身。有幾件事情需要考慮:
有些東西需要呼叫該
update方法。通常,這是由repaint()對組件的呼叫觸發的,但此代碼中沒有任何內容呼叫該方法。update呼叫重寫的方法以super.update(g)確保呼叫默認行為(清除畫布并再次繪制)很重要。影片具有時間分量:橢圓應該在一段時間內移動。這需要納入邏輯。AWT 沒有內置的定時行為機制。
如果您能夠使用 Swing 中的
javax.swing.Timer類,則該類對于影片非常有用。它在 AWT 執行緒上執行您的回呼,因此意味著您不必采取特殊措施來確保執行緒安全。如果您不能使用 Swing,它可以使用
java.util.Timer或 自定義執行緒,但需要直接管理執行緒同步。您可能還希望在橢圓到達畫布邊緣時停止影片。
這是一個使用示例javax.swing.Timer(假設 Java 8 或更高版本)。請注意,所有影片邏輯都在ActionListener附加到 中Timer,因此update已洗掉覆寫的方法:
import javax.swing.*;
import java.awt.*;
public class MainClass {
public static final int CANVAS_SIZE = 400;
public MainClass() {
Frame f = new Frame("Canvas Example");
f.add(new MyCanvas(CANVAS_SIZE));
f.setLayout(null);
f.setSize(CANVAS_SIZE, CANVAS_SIZE);
f.setVisible(true);
}
public static void main(String[] args) {
new MainClass();
}
}
class MyCanvas extends Canvas {
public static final int INITIAL_POSITION = 75;
public static final int HEIGHT = 75;
public static final int WIDTH = 150;
private static final int TIMER_DELAY_MILLIS = 1000 / 30; // 30 FPS
private int x = INITIAL_POSITION;
private final Timer timer;
public MyCanvas(int canvasSize) {
setBackground(Color.BLACK);
setSize(canvasSize, canvasSize);
timer = new Timer(TIMER_DELAY_MILLIS, (event) -> {
// ensure the oval stays on the canvas
if (x WIDTH < getWidth()) {
x ;
repaint();
} else {
stopAnimation();
}
});
timer.start();
}
public void paint(Graphics g) {
g.setColor(Color.red);
g.fillOval(x, INITIAL_POSITION, WIDTH, HEIGHT);
}
private void stopAnimation() {
timer.stop();
}
}
此代碼有一些額外的附帶更改。
- 更新了
mainClasstoMainClass(首字母大寫“M”)的名稱以符合標準 Java 命名約定。 - 改變
String args[]以String[] args出于同樣的原因。 - 將數字常量提取到命名
static final欄位。 - 使畫布大小成為建構式引數,由呼叫者控制。
- 做
x私人。 - 微小的格式更改以確保一致的樣式。
一種不使用的選項javax.swing.Timer(省略未更改的代碼):
private final AtomicInteger x = new AtomicInteger(INITIAL_POSITION);
public MyCanvas(int canvasSize) {
setBackground(Color.BLACK);
setSize(canvasSize, canvasSize);
new Thread(() -> {
try {
// ensure the oval stays on the canvas
while (x.incrementAndGet() WIDTH < getWidth()) {
Thread.sleep(TIMER_DELAY_MILLIS);
repaint();
}
} catch (InterruptedException e) {
// Just let the thread exit
Thread.currentThread().interrupt();
}
}).start();
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/393685.html
下一篇:Heroku反應部署
