如何在 java 中缓慢移动我的子弹?
HOw to move my bullet slowly in java?
您好,我正在开发一款战斗机左右移动并射击的游戏。对于射击部分,我尝试使用 for 循环来减慢速度,让用户可以看到子弹。但这还不够。我也用过 sleep 但不是一个好的答案。现在我不知道该怎么办。
这是我的 paintComponent 类:
package game;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class PaintComponent extends JPanel implements KeyListener {
int dx = 200-15;
int dy = 450;
int my = 450;
ArrayList<Bullet> bullets = new ArrayList<>();
public Rectangle2D rec =new Rectangle2D.Double(dx , dy, 30, 10);
Rectangle2D recB = new Rectangle2D.Double(dx+13 , my, 6, 6);
// public Polygon pol = new Polygon
private BufferedImage imageBg, imageFi, imageBu;
public PaintComponent() {
this.addKeyListener(this);
this.setFocusable(true);
this.setBackground(Color.white);
try {
imageBg = ImageIO.read(new File("C:\Java\NetbeansProjects\Game\bg.jpg"));
imageBu = ImageIO.read(new File("C:\Java\NetbeansProjects\Game\bul.png"));
imageFi = ImageIO.read(new File("C:\Java\NetbeansProjects\Game\fi.png"));
} catch (IOException ex) {
System.out.println("No background image is available!");
}
}
public void shoot(){
if(bullets != null){
for(int i=0; i<bullets.size(); i++){
for(int j=0; j<200; j++){
bullets.get(i).setdy(my-j);
}
System.out.println(bullets.get(i).getdy());
}
}
}
public void moveRec(KeyEvent evt){
switch(evt.getKeyCode()){
case KeyEvent.VK_LEFT:
dx -= 5;
rec.setRect(dx, dy, 30, 10);
recB.setRect(dx+13, dy, 6, 6);
repaint();
break;
case KeyEvent.VK_RIGHT:
dx += 5;
rec.setRect(dx, dy, 30, 10);
recB.setRect(dx+13, dy, 6, 6);
repaint();
break;
case KeyEvent.VK_SPACE:
Bullet b = new Bullet(dx, dy);
bullets.add(b);
shoot();
break;
}
}
@Override
public void paintComponent(Graphics grphcs)
{super.paintComponent(grphcs);
Graphics2D gr = (Graphics2D) grphcs;
int X = (int) rec.getCenterX();
int Y = (int) rec.getCenterY();
gr.drawImage(imageBg, 0, 0, null);
gr.drawImage(imageFi, X-50, Y-75, null);
gr.setColor(Color.GRAY);
if(bullets != null){
for(int i=0;i<bullets.size();i++){
gr.drawImage(imageBu, null, bullets.get(i).getdx(), bullets.get(i).getdy());
repaint();
}
}
gr.draw(recB);
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e)
{moveRec(e);}
@Override
public void keyReleased(KeyEvent e)
{}
}
这是我的项目符号:
package game;
public class Bullet {
private int x, y, newy;
public Bullet(int x, int y){
this.x = x;
this.y = y;
}
public int getdy(){
return y;
}
public void setdy(int newy){
y = newy;
}
public int getdx(){
return x;
}
}
在你的游戏循环中让它每隔这么多刻更新一次子弹。您很可能永远不想使用 sleep 来减慢游戏中的某些内容,因为它需要一个新线程,否则它会在整个游戏中休眠。
如果您不知道什么是游戏循环,游戏循环基本上就是一个循环,不断获取输入、更新游戏(例如子弹)、渲染所有内容,然后暂停整个程序(您可以使用sleep) 循环剩余的时间量。您还想根据自上次更新以来经过的滴答数或毫秒数来更新游戏。这将防止游戏在不同计算机上 运行 更快或更慢。
我还没有完全读完这篇文章,但 THIS 可能会有所帮助。
此外,不确定这是否正确,我认为使用 Canvas
而不是 JPanel
更适合制作游戏。这是我在第一个 java 游戏中使用的。
编辑:
如果你想使用 JPanel 而没有游戏循环,你可以让 shoot 方法创建一个新线程并使用 sleep 来减慢它的速度。此外,如果射击多颗子弹,设置射击方法的方式可能会导致问题,因为它将在两个单独的循环中循环遍历每颗子弹,如果您有一个单独的线程,则可以在一个线程中循环时修改子弹数组,从而导致一个错误。
试试这个:
public void shoot(Bullet bullet){
new Thread(new Runnable(){
for(int j=0; j<200; j++){
bullet.setdy(my-j);
try{
Thread.sleep(time);//set the time
catch(Exception e){
e.printStackTrace();
}
}
System.out.println(bullet.getdy());
getBullets().remove(bullet);
}).start();
}
创建一个名为 getBullets() 的方法。确保它有同步修饰符。
protected synchronized ArrayList<Bullet> getBullets(){
return bullets;
}
这将确保它一次只能由一个线程修改。
最后在玩家按下space的地方,将bullets.add(b);
改为getBullets().add(b);
我认为你放慢循环速度是错误的。您最不想做的就是放慢游戏循环或让游戏循环休眠。这将影响你在游戏中的所有对象。
有多种方法可以解决这个问题:
每次刻度的增量较小
您可以做的最明显的事情之一就是减小子弹的增量。让我们看看你的 shoot();方法:
public void shoot(){
if(bullets != null){
for(int i=0; i<bullets.size(); i++){
for(int j=0; j<200; j++){
bullets.get(i).setdy(my-j);
}
System.out.println(bullets.get(i).getdy());
}
}
}
据我所知,您对所有项目符号进行了 200 次迭代,项目符号的 y 轴的每个刻度都会发生变化,使用公式“my - j”或“450 - 刻度指数”
为了减慢子弹的速度,您需要将 j 除以一定的数字以获得所需的子弹速度。例如:“my - (j / 2)”会影响子弹的速度。尝试使用这些变量来获得所需的速度。
添加速度修改器
很多游戏都需要一个速度修改器或每个射弹的基本速度。这可能对你有用,我唯一注意到你有点试图模拟速度损失。要实现此结果,您需要另一个变量。我们暂时称其为“生存时间”。
因此,如果我们修改项目符号 class,它将看起来像这样。注意到我们还有一个名为 Move(); 的新函数。这将根据变量计算下一步行动。
public class Bullet {
private int x, y, newy;
private speed, ttl; //ttl = time to live
public Bullet(int x, int y, int speed){
this.x = x;
this.y = y;
this.speed= speed;
this.ttl = 250;
}
public int Move()
{
//Do some calculation to perform loss of velocity within a reasonable range. Because these number might be overkill
this.speed -= (ttl / 100);
y += this.speed;
ttl--;
}
public int getdy(){
return y;
}
public void setdy(int newy){
y = newy;
}
public int getdx(){
return x;
}
}
代码现在所做的是根据生存时间变量计算速度,子弹生存时间越长,速度就越慢。调整速度变量可以让您更好地控制子弹。我自己也这么说,拍摄方法看起来更整洁:
public void shoot(){
if(bullets != null){
for(int i=0; i<bullets.size(); i++){
bullets.get(i).Move();
}
}
}
当然还有更多,比如检查速度和生存时间是否超出范围之类的,但我认为你足够聪明,可以解决这个问题;)
运行 关闭定时器
正如 ControlAltDel 所说,您可以实现一个计时器,我不是 java 方面的专家,因此我不会对此进行深入探讨。但它肯定是有可能的。无非就是在定时器的tick函数里面实现当前的shoot方法。当然删除 for i<200 循环。因为它不是很有效。
总之
如果我确实弄错了或误解了(甚至语法错误:))问题,我很抱歉。如果有任何问题,我很乐意听取您的意见 ;)。
请注意,这是未经测试的代码,我只是在这里解释您可以尝试使其按预期工作的事情!
此致,
合成。
更新:
一些关于如何更新项目符号的解释。
为了更新项目符号,我们需要使其 运行 脱离循环。因为在这种情况下,主循环也是绘图发生的地方,即“paintComponent”方法。绘制组件已经有一个循环来绘制子弹,我们唯一要做的就是添加关于 .Move(); 的逻辑。方法。
绘画组件将如下所示(+ 我还修复了制表符):
@Override
public void paintComponent(Graphics grphcs)
{
super.paintComponent(grphcs);
Graphics2D gr = (Graphics2D) grphcs;
int X = (int) rec.getCenterX();
int Y = (int) rec.getCenterY();
gr.drawImage(imageBg, 0, 0, null);
gr.drawImage(imageFi, X-50, Y-75, null);
gr.setColor(Color.GRAY);
if(bullets != null)
{
for(int i=0;i<bullets.size();i++)
{
//Here is were we loop over the bullet list, lets add the move method
bullets.get(i).Move();
gr.drawImage(imageBu, null, bullets.get(i).getdx(), bullets.get(i).getdy());
repaint();
}
}
gr.draw(recB);
}
添加的东西是“bullets.get(i).Move();”。现在每帧 运行。这在理论上是可行的(inb4 im 不测试这些代码)。假设您使用了子弹的多个实例 class,每个 class 都应该封装自己的速度和生存时间变量。
执行此操作将使 shoot 方法过时。您可以做的是将与拍摄相关的代码移至 paintComponent 中,并将其移至拍摄功能。
关于生存时间变量,我想在代码中再添加一段。这将负责子弹的垃圾收集。从现在起他们无限期地生活:
for(int i=0;i<bullets.size();i++)
{
Bullet b = bullets.get(i);
if(b.ttl >= 1)
{
bullets.get(i).Move();
gr.drawImage(imageBu, null, b.getdx(), b.getdy());
}
else
{
//Remove the bullet from the list
//In C# its bullets.Remove(b);
}
repaint();
}
希望这能解决子弹不动的问题。以及由于子弹未被销毁而导致的潜在性能问题。以前,有任何问题我都喜欢听他们说! ;)
最后,我在我的项目符号 class 中添加了一个计时器,并在我的 paintcomponent 方法中添加了 repaint()!
package game;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class Bullet {
private int x, y;
private int speed, ttl;
public final Timer timer1;
public Bullet(int x, int y, int speed){
this.x = x;
this.y = y;
this.speed= speed;
this.ttl = 250;
ActionListener actListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Move();
}
};
this.timer1 = new Timer(50, actListener);
timer1.start();
}
public int getdy(){
return y;
}
public void setdy(int newy){
y = newy;
}
public int getdx(){
return x;
}
public int Move(){
//Do some calculation to perform loss of velocity within a reasonable range. Because these number might be overkill
this.speed -= (ttl / 100);
y += this.speed;
ttl--;
return y;
}
}
您好,我正在开发一款战斗机左右移动并射击的游戏。对于射击部分,我尝试使用 for 循环来减慢速度,让用户可以看到子弹。但这还不够。我也用过 sleep 但不是一个好的答案。现在我不知道该怎么办。 这是我的 paintComponent 类:
package game;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class PaintComponent extends JPanel implements KeyListener {
int dx = 200-15;
int dy = 450;
int my = 450;
ArrayList<Bullet> bullets = new ArrayList<>();
public Rectangle2D rec =new Rectangle2D.Double(dx , dy, 30, 10);
Rectangle2D recB = new Rectangle2D.Double(dx+13 , my, 6, 6);
// public Polygon pol = new Polygon
private BufferedImage imageBg, imageFi, imageBu;
public PaintComponent() {
this.addKeyListener(this);
this.setFocusable(true);
this.setBackground(Color.white);
try {
imageBg = ImageIO.read(new File("C:\Java\NetbeansProjects\Game\bg.jpg"));
imageBu = ImageIO.read(new File("C:\Java\NetbeansProjects\Game\bul.png"));
imageFi = ImageIO.read(new File("C:\Java\NetbeansProjects\Game\fi.png"));
} catch (IOException ex) {
System.out.println("No background image is available!");
}
}
public void shoot(){
if(bullets != null){
for(int i=0; i<bullets.size(); i++){
for(int j=0; j<200; j++){
bullets.get(i).setdy(my-j);
}
System.out.println(bullets.get(i).getdy());
}
}
}
public void moveRec(KeyEvent evt){
switch(evt.getKeyCode()){
case KeyEvent.VK_LEFT:
dx -= 5;
rec.setRect(dx, dy, 30, 10);
recB.setRect(dx+13, dy, 6, 6);
repaint();
break;
case KeyEvent.VK_RIGHT:
dx += 5;
rec.setRect(dx, dy, 30, 10);
recB.setRect(dx+13, dy, 6, 6);
repaint();
break;
case KeyEvent.VK_SPACE:
Bullet b = new Bullet(dx, dy);
bullets.add(b);
shoot();
break;
}
}
@Override
public void paintComponent(Graphics grphcs)
{super.paintComponent(grphcs);
Graphics2D gr = (Graphics2D) grphcs;
int X = (int) rec.getCenterX();
int Y = (int) rec.getCenterY();
gr.drawImage(imageBg, 0, 0, null);
gr.drawImage(imageFi, X-50, Y-75, null);
gr.setColor(Color.GRAY);
if(bullets != null){
for(int i=0;i<bullets.size();i++){
gr.drawImage(imageBu, null, bullets.get(i).getdx(), bullets.get(i).getdy());
repaint();
}
}
gr.draw(recB);
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e)
{moveRec(e);}
@Override
public void keyReleased(KeyEvent e)
{}
}
这是我的项目符号:
package game;
public class Bullet {
private int x, y, newy;
public Bullet(int x, int y){
this.x = x;
this.y = y;
}
public int getdy(){
return y;
}
public void setdy(int newy){
y = newy;
}
public int getdx(){
return x;
}
}
在你的游戏循环中让它每隔这么多刻更新一次子弹。您很可能永远不想使用 sleep 来减慢游戏中的某些内容,因为它需要一个新线程,否则它会在整个游戏中休眠。
如果您不知道什么是游戏循环,游戏循环基本上就是一个循环,不断获取输入、更新游戏(例如子弹)、渲染所有内容,然后暂停整个程序(您可以使用sleep) 循环剩余的时间量。您还想根据自上次更新以来经过的滴答数或毫秒数来更新游戏。这将防止游戏在不同计算机上 运行 更快或更慢。
我还没有完全读完这篇文章,但 THIS 可能会有所帮助。
此外,不确定这是否正确,我认为使用 Canvas
而不是 JPanel
更适合制作游戏。这是我在第一个 java 游戏中使用的。
编辑:
如果你想使用 JPanel 而没有游戏循环,你可以让 shoot 方法创建一个新线程并使用 sleep 来减慢它的速度。此外,如果射击多颗子弹,设置射击方法的方式可能会导致问题,因为它将在两个单独的循环中循环遍历每颗子弹,如果您有一个单独的线程,则可以在一个线程中循环时修改子弹数组,从而导致一个错误。
试试这个:
public void shoot(Bullet bullet){
new Thread(new Runnable(){
for(int j=0; j<200; j++){
bullet.setdy(my-j);
try{
Thread.sleep(time);//set the time
catch(Exception e){
e.printStackTrace();
}
}
System.out.println(bullet.getdy());
getBullets().remove(bullet);
}).start();
}
创建一个名为 getBullets() 的方法。确保它有同步修饰符。
protected synchronized ArrayList<Bullet> getBullets(){
return bullets;
}
这将确保它一次只能由一个线程修改。
最后在玩家按下space的地方,将bullets.add(b);
改为getBullets().add(b);
我认为你放慢循环速度是错误的。您最不想做的就是放慢游戏循环或让游戏循环休眠。这将影响你在游戏中的所有对象。
有多种方法可以解决这个问题:
每次刻度的增量较小
您可以做的最明显的事情之一就是减小子弹的增量。让我们看看你的 shoot();方法:
public void shoot(){
if(bullets != null){
for(int i=0; i<bullets.size(); i++){
for(int j=0; j<200; j++){
bullets.get(i).setdy(my-j);
}
System.out.println(bullets.get(i).getdy());
}
}
}
据我所知,您对所有项目符号进行了 200 次迭代,项目符号的 y 轴的每个刻度都会发生变化,使用公式“my - j”或“450 - 刻度指数”
为了减慢子弹的速度,您需要将 j 除以一定的数字以获得所需的子弹速度。例如:“my - (j / 2)”会影响子弹的速度。尝试使用这些变量来获得所需的速度。
添加速度修改器
很多游戏都需要一个速度修改器或每个射弹的基本速度。这可能对你有用,我唯一注意到你有点试图模拟速度损失。要实现此结果,您需要另一个变量。我们暂时称其为“生存时间”。
因此,如果我们修改项目符号 class,它将看起来像这样。注意到我们还有一个名为 Move(); 的新函数。这将根据变量计算下一步行动。
public class Bullet {
private int x, y, newy;
private speed, ttl; //ttl = time to live
public Bullet(int x, int y, int speed){
this.x = x;
this.y = y;
this.speed= speed;
this.ttl = 250;
}
public int Move()
{
//Do some calculation to perform loss of velocity within a reasonable range. Because these number might be overkill
this.speed -= (ttl / 100);
y += this.speed;
ttl--;
}
public int getdy(){
return y;
}
public void setdy(int newy){
y = newy;
}
public int getdx(){
return x;
}
}
代码现在所做的是根据生存时间变量计算速度,子弹生存时间越长,速度就越慢。调整速度变量可以让您更好地控制子弹。我自己也这么说,拍摄方法看起来更整洁:
public void shoot(){
if(bullets != null){
for(int i=0; i<bullets.size(); i++){
bullets.get(i).Move();
}
}
}
当然还有更多,比如检查速度和生存时间是否超出范围之类的,但我认为你足够聪明,可以解决这个问题;)
运行 关闭定时器
正如 ControlAltDel 所说,您可以实现一个计时器,我不是 java 方面的专家,因此我不会对此进行深入探讨。但它肯定是有可能的。无非就是在定时器的tick函数里面实现当前的shoot方法。当然删除 for i<200 循环。因为它不是很有效。
总之
如果我确实弄错了或误解了(甚至语法错误:))问题,我很抱歉。如果有任何问题,我很乐意听取您的意见 ;)。
请注意,这是未经测试的代码,我只是在这里解释您可以尝试使其按预期工作的事情!
此致,
合成。
更新:
一些关于如何更新项目符号的解释。 为了更新项目符号,我们需要使其 运行 脱离循环。因为在这种情况下,主循环也是绘图发生的地方,即“paintComponent”方法。绘制组件已经有一个循环来绘制子弹,我们唯一要做的就是添加关于 .Move(); 的逻辑。方法。
绘画组件将如下所示(+ 我还修复了制表符):
@Override
public void paintComponent(Graphics grphcs)
{
super.paintComponent(grphcs);
Graphics2D gr = (Graphics2D) grphcs;
int X = (int) rec.getCenterX();
int Y = (int) rec.getCenterY();
gr.drawImage(imageBg, 0, 0, null);
gr.drawImage(imageFi, X-50, Y-75, null);
gr.setColor(Color.GRAY);
if(bullets != null)
{
for(int i=0;i<bullets.size();i++)
{
//Here is were we loop over the bullet list, lets add the move method
bullets.get(i).Move();
gr.drawImage(imageBu, null, bullets.get(i).getdx(), bullets.get(i).getdy());
repaint();
}
}
gr.draw(recB);
}
添加的东西是“bullets.get(i).Move();”。现在每帧 运行。这在理论上是可行的(inb4 im 不测试这些代码)。假设您使用了子弹的多个实例 class,每个 class 都应该封装自己的速度和生存时间变量。
执行此操作将使 shoot 方法过时。您可以做的是将与拍摄相关的代码移至 paintComponent 中,并将其移至拍摄功能。
关于生存时间变量,我想在代码中再添加一段。这将负责子弹的垃圾收集。从现在起他们无限期地生活:
for(int i=0;i<bullets.size();i++)
{
Bullet b = bullets.get(i);
if(b.ttl >= 1)
{
bullets.get(i).Move();
gr.drawImage(imageBu, null, b.getdx(), b.getdy());
}
else
{
//Remove the bullet from the list
//In C# its bullets.Remove(b);
}
repaint();
}
希望这能解决子弹不动的问题。以及由于子弹未被销毁而导致的潜在性能问题。以前,有任何问题我都喜欢听他们说! ;)
最后,我在我的项目符号 class 中添加了一个计时器,并在我的 paintcomponent 方法中添加了 repaint()!
package game;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class Bullet {
private int x, y;
private int speed, ttl;
public final Timer timer1;
public Bullet(int x, int y, int speed){
this.x = x;
this.y = y;
this.speed= speed;
this.ttl = 250;
ActionListener actListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Move();
}
};
this.timer1 = new Timer(50, actListener);
timer1.start();
}
public int getdy(){
return y;
}
public void setdy(int newy){
y = newy;
}
public int getdx(){
return x;
}
public int Move(){
//Do some calculation to perform loss of velocity within a reasonable range. Because these number might be overkill
this.speed -= (ttl / 100);
y += this.speed;
ttl--;
return y;
}
}