如何在 libgdx 中创建自动操作? (场景2d)
How do I create automatic actions in libgdx? (scene2d)
我是 libgdx 和 android 开发的新手。我正在编写我的第一款游戏,一款用于体验原始 "Simon" 游戏的游戏。我正在使用 scene2d,将每个按钮用作一个演员,每个按钮的构造函数中都有 ClickListeners。
我现在真正苦苦挣扎的是如何 "perform" AI 序列,例如:我需要在计算机上直观地显示 "pressing" 每个回合的按钮。我能够播放声音并将一个整数附加到 ArrayList,但它似乎只会在绘制我的纹理之前执行此操作。
所以当我启动程序时,我会听到一声随机的哔哔声,并在日志中看到一个随机数附加到我的 ArrayList,然后我最终会看到我的演员准备好 clicked/touched。我对如何解决这个问题感到困惑。这是我游戏中的一些代码。请原谅我的草率,因为我是初学者,而且它还在进行中。可能有更好的方法来实现我正在尝试做的事情,但就目前而言,我只想让它发挥作用。
我了解了一些关于 Actions 的知识,但我似乎无法让它发挥作用。似乎他们需要一些输入才能执行操作。
注意:我将未点亮和点亮的按钮与最后创建的未点亮版本堆叠在一起,以便在我将未点亮按钮的可见性设置为 false 后点亮按钮出现。我意识到这可能不是最好的方法。
这是我的主要游戏class:
import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.utils.viewport.FitViewport;
import java.util.ArrayList;
public class XimonGame extends Game {
private SpriteBatch batch;
private Stage stage;
private OrthographicCamera camera;
XimonButton actor_g;
XimonButton actor_gLit;
XimonButton actor_r;
XimonButton actor_rLit;
XimonButton actor_y;
XimonButton actor_yLit;
XimonButton actor_b;
XimonButton actor_bLit;
public static ArrayList<Integer> playerList;
public static ArrayList<Integer> computerList;
public static ButtonSequencer buttonSequencer;
@Override
public void create () {
batch = new SpriteBatch();
// Creating camera
camera = new OrthographicCamera(800, 480);
// Creating stage and actors
stage = new Stage(new FitViewport(800, 480, camera));
// Creating ArrayLists & ButtonSequencer
playerList = new ArrayList<Integer>();
computerList = new ArrayList<Integer>();
buttonSequencer = new ButtonSequencer();
// Appending initial random number to computerList:
buttonSequencer.appendRandomNumber(1, 4, computerList);
System.out.println("Created computer's ArrayList.\nInitial number is: "
+ computerList.toString());
System.out.println("Size of computerList is currently " + computerList.size());
// Creating (constructing) actors. Parameter info:
// XimonButton("[png file]", x, y, width, height, "[name]")
actor_g = new XimonButton("img/green.png", 190, 240, 210, 210, 1);
System.out.println(actor_g.toString() + " created.");
// Lit version of button
actor_gLit = new XimonButton("img/green_on.png", 190, 240, 210, 210, 0);
actor_r = new XimonButton("img/red.png", 400, 240, 210, 210, 2);
System.out.println(actor_r.toString() + " created.");
// Lit version of button
actor_rLit = new XimonButton("img/red_on.png", 400, 240, 210, 210, 0);
actor_y = new XimonButton("img/yellow.png", 190, 30, 210, 210, 3);
System.out.println(actor_y.toString() + " created.");
// Lit version of button
actor_yLit = new XimonButton("img/yellow_on.png", 190, 30, 210, 210, 0);
actor_b = new XimonButton("img/blue.png", 400, 30, 210, 210, 4);
System.out.println(actor_b.toString() + " created.");
// Lit version of button
actor_bLit = new XimonButton("img/blue_on.png", 400, 30, 210, 210, 0);
// ADDING ACTORS
//
// Green buttons:
stage.addActor(actor_gLit);
stage.addActor(actor_g);
// Red buttons:
stage.addActor(actor_rLit);
stage.addActor(actor_r);
// Yellow buttons:
stage.addActor(actor_yLit);
stage.addActor(actor_y);
// Blue buttons:
stage.addActor(actor_bLit);
stage.addActor(actor_b);
Gdx.input.setInputProcessor(stage);
}
@Override
public void render () {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
batch.begin();
super.render();
batch.end();
// Setting fullscreen
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
// Restore stage's viewport
stage.getViewport().update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
}
@Override
public void setScreen(Screen screen) {
super.setScreen(screen);
}
@Override
public Screen getScreen() {
return super.getScreen();
}
@Override
public void resize (int width, int height) {
super.resize(width, height);
}
@Override
public void pause () {
super.pause();
}
@Override
public void resume () {
super.resume();
}
public void dispose () {
stage.dispose();
}
}
这是我的 Actor class 按钮:
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Touchable;
import com.badlogic.gdx.scenes.scene2d.actions.RunnableAction;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.mygdx.game.XimonGame;
import java.util.ArrayList;
class XimonButton extends Actor {
Sprite sprite;
private Sound sound;
private final int num;
private int count;
private final int code;
public static boolean isComputerTurn = true;
public XimonButton (String file, int x, int y, int w, int h, final int colorCode) {
sprite = new Sprite(new Texture(Gdx.files.internal(file)));
this.setBounds(x, y, w, h);
this.num = colorCode;
count = 0;
code = colorCode;
if (colorCode == 1) {
sound = Gdx.audio.newSound(Gdx.files.internal("sounds/Green.wav"));
}
else if (colorCode == 2) {
sound = Gdx.audio.newSound(Gdx.files.internal("sounds/Red.wav"));
}
else if (colorCode == 3) {
sound = Gdx.audio.newSound(Gdx.files.internal("sounds/Yellow.wav"));
}
else if (colorCode == 4) {
sound = Gdx.audio.newSound(Gdx.files.internal("sounds/Blue.wav"));
}
setTouchable(Touchable.disabled);
// Perform the AI's turn:
try {
performAISequence();
} catch (InterruptedException e) {
e.printStackTrace();
}
// Used to handle touch input
addListener(new ClickListener() {
@Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
sound.play(0.4f);
if (XimonButton.this.isVisible()) XimonButton.this.setVisible(false);
// System.out.println("Current player list contains:");
// for (int i=0; i < playerList.size(); i++) {
// System.out.println(playerList.get(i));
// }
return true;
}
@Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
// Stop the sound and "turn off" the button light
sound.stop();
XimonButton.this.setVisible(true);
// Count the number of touches for this turn
XimonGame.buttonSequencer.incrementCount();
System.out.println("Count is: " + XimonGame.buttonSequencer.getCount());
System.out.println("Color code is: " + colorCode);
// Append the colorCode to the playerList.
// Loop through the computer list to check if current touch matches
// the computer's num for the index in the ArrayList.
XimonGame.playerList.add(colorCode);
System.out.println("Player list is: " + XimonGame.playerList.toString() + "\n");
//
}
});
}
public void performAISequence () throws InterruptedException {
setTouchable(Touchable.disabled);
for (int i = 0; i < XimonGame.computerList.size(); i++) {
if (code == XimonGame.computerList.get(i)) {
sound.play(0.4f);
XimonButton.this.setVisible(false);
Thread.sleep(XimonGame.buttonSequencer.getRandomInt(400,600));
sound.stop();
XimonButton.this.setVisible(true);
Thread.sleep(XimonGame.buttonSequencer.getRandomInt(100,150));
}
}
setTouchable(Touchable.enabled);
}
@Override
public void draw (Batch batch, float parentAlpha) {
batch.draw(sprite, this.getX(), this.getY(), this.getWidth(), this.getHeight());
}
@Override
public void act(float delta) {
super.act(delta);
}
}
另外:
import java.util.ArrayList;
import java.util.Random;
public class ButtonSequencer {
public Random rand;
private int turn = 0;
private int count;
// public static void addPlayerNums(int code) {
// XimonGame.playerList.add(code);
// }
public int getCount() {
return count;
}
public void incrementCount() {
count++;
}
public void appendRandomNumber(int min, int max, ArrayList<Integer> computerList) {
rand = new Random();
int random = rand.nextInt((max - min) + 1) + min;
computerList.add(random);
turn++;
}
public int getRandomInt(int min, int max) {
rand = new Random();
return rand.nextInt((max - min) + 1) + min;
}
}
很抱歉粘贴了这么多代码,但如果有什么问题,我不知道它在哪里或做什么,所以我认为分享大部分代码是个好主意。
代码太多,我无法对所有内容进行评论,但我看到的最大明显错误是 Thread.sleep
的使用。这将导致您的游戏冻结,直到线程完成休眠。
Libgdx 不是为多线程构建的,因此您需要考虑在计时器上设置内容。需要一段时间的事件需要有在渲染循环的每个 运行 上递减的计时器,并在它们的计时器 运行 关闭时触发它们的事件。
由于您使用的是 stage2d,这变得非常容易,因为 Actions 有自己的计时器,您可以按顺序将它们链接在一起。继续阅读 stage2d's Actions system。您可以创建自己的动作并将它们与中间带有 DelayActions 的 SequenceAction 链接在一起。
举个例子。我不太熟悉你的整个游戏结构,所以我不确定这是否适用于你的代码,但它应该让你对如何做到这一点有一个基本的了解。
public void performAISequence (){
//Get a sequence action from the pool (to avoid unnecessary allocation)
//and give it the first action
SequenceAction sequence = Actions.sequence(Actions.touchable(Touchable.disabled));
for (int i = 0; i < XimonGame.computerList.size(); i++) {
if (code == XimonGame.computerList.get(i)) {
sequence.addAction(Actions.runnable(playSoundRunnable));
sequence.addAction(Actions.hide());
}
//Want to delay it for every item in list, because it may need to wait for other buttons
sequence.addAction(Actions.delay(BUTTON_DELAY_TIME));
if (code == XimonGame.computerList.get(i)) {
sequence.addAction(Actions.show());
sequence.addAction(Actions.runnable(stopSoundRunnable));
}
sequence.addAction(Actions.touchable(Touchable.enabled));
}
this.addAction(sequence);
}
Runnable playSoundRunnable = new Runnable(){
public void run(){
sound.play(0.4f);
}
};
Runnable stopSoundRunnable = new Runnable(){
public void run(){
sound.stop();
}
};
我是 libgdx 和 android 开发的新手。我正在编写我的第一款游戏,一款用于体验原始 "Simon" 游戏的游戏。我正在使用 scene2d,将每个按钮用作一个演员,每个按钮的构造函数中都有 ClickListeners。
我现在真正苦苦挣扎的是如何 "perform" AI 序列,例如:我需要在计算机上直观地显示 "pressing" 每个回合的按钮。我能够播放声音并将一个整数附加到 ArrayList,但它似乎只会在绘制我的纹理之前执行此操作。 所以当我启动程序时,我会听到一声随机的哔哔声,并在日志中看到一个随机数附加到我的 ArrayList,然后我最终会看到我的演员准备好 clicked/touched。我对如何解决这个问题感到困惑。这是我游戏中的一些代码。请原谅我的草率,因为我是初学者,而且它还在进行中。可能有更好的方法来实现我正在尝试做的事情,但就目前而言,我只想让它发挥作用。
我了解了一些关于 Actions 的知识,但我似乎无法让它发挥作用。似乎他们需要一些输入才能执行操作。
注意:我将未点亮和点亮的按钮与最后创建的未点亮版本堆叠在一起,以便在我将未点亮按钮的可见性设置为 false 后点亮按钮出现。我意识到这可能不是最好的方法。
这是我的主要游戏class:
import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.utils.viewport.FitViewport;
import java.util.ArrayList;
public class XimonGame extends Game {
private SpriteBatch batch;
private Stage stage;
private OrthographicCamera camera;
XimonButton actor_g;
XimonButton actor_gLit;
XimonButton actor_r;
XimonButton actor_rLit;
XimonButton actor_y;
XimonButton actor_yLit;
XimonButton actor_b;
XimonButton actor_bLit;
public static ArrayList<Integer> playerList;
public static ArrayList<Integer> computerList;
public static ButtonSequencer buttonSequencer;
@Override
public void create () {
batch = new SpriteBatch();
// Creating camera
camera = new OrthographicCamera(800, 480);
// Creating stage and actors
stage = new Stage(new FitViewport(800, 480, camera));
// Creating ArrayLists & ButtonSequencer
playerList = new ArrayList<Integer>();
computerList = new ArrayList<Integer>();
buttonSequencer = new ButtonSequencer();
// Appending initial random number to computerList:
buttonSequencer.appendRandomNumber(1, 4, computerList);
System.out.println("Created computer's ArrayList.\nInitial number is: "
+ computerList.toString());
System.out.println("Size of computerList is currently " + computerList.size());
// Creating (constructing) actors. Parameter info:
// XimonButton("[png file]", x, y, width, height, "[name]")
actor_g = new XimonButton("img/green.png", 190, 240, 210, 210, 1);
System.out.println(actor_g.toString() + " created.");
// Lit version of button
actor_gLit = new XimonButton("img/green_on.png", 190, 240, 210, 210, 0);
actor_r = new XimonButton("img/red.png", 400, 240, 210, 210, 2);
System.out.println(actor_r.toString() + " created.");
// Lit version of button
actor_rLit = new XimonButton("img/red_on.png", 400, 240, 210, 210, 0);
actor_y = new XimonButton("img/yellow.png", 190, 30, 210, 210, 3);
System.out.println(actor_y.toString() + " created.");
// Lit version of button
actor_yLit = new XimonButton("img/yellow_on.png", 190, 30, 210, 210, 0);
actor_b = new XimonButton("img/blue.png", 400, 30, 210, 210, 4);
System.out.println(actor_b.toString() + " created.");
// Lit version of button
actor_bLit = new XimonButton("img/blue_on.png", 400, 30, 210, 210, 0);
// ADDING ACTORS
//
// Green buttons:
stage.addActor(actor_gLit);
stage.addActor(actor_g);
// Red buttons:
stage.addActor(actor_rLit);
stage.addActor(actor_r);
// Yellow buttons:
stage.addActor(actor_yLit);
stage.addActor(actor_y);
// Blue buttons:
stage.addActor(actor_bLit);
stage.addActor(actor_b);
Gdx.input.setInputProcessor(stage);
}
@Override
public void render () {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
batch.begin();
super.render();
batch.end();
// Setting fullscreen
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
// Restore stage's viewport
stage.getViewport().update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
}
@Override
public void setScreen(Screen screen) {
super.setScreen(screen);
}
@Override
public Screen getScreen() {
return super.getScreen();
}
@Override
public void resize (int width, int height) {
super.resize(width, height);
}
@Override
public void pause () {
super.pause();
}
@Override
public void resume () {
super.resume();
}
public void dispose () {
stage.dispose();
}
}
这是我的 Actor class 按钮:
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Touchable;
import com.badlogic.gdx.scenes.scene2d.actions.RunnableAction;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.mygdx.game.XimonGame;
import java.util.ArrayList;
class XimonButton extends Actor {
Sprite sprite;
private Sound sound;
private final int num;
private int count;
private final int code;
public static boolean isComputerTurn = true;
public XimonButton (String file, int x, int y, int w, int h, final int colorCode) {
sprite = new Sprite(new Texture(Gdx.files.internal(file)));
this.setBounds(x, y, w, h);
this.num = colorCode;
count = 0;
code = colorCode;
if (colorCode == 1) {
sound = Gdx.audio.newSound(Gdx.files.internal("sounds/Green.wav"));
}
else if (colorCode == 2) {
sound = Gdx.audio.newSound(Gdx.files.internal("sounds/Red.wav"));
}
else if (colorCode == 3) {
sound = Gdx.audio.newSound(Gdx.files.internal("sounds/Yellow.wav"));
}
else if (colorCode == 4) {
sound = Gdx.audio.newSound(Gdx.files.internal("sounds/Blue.wav"));
}
setTouchable(Touchable.disabled);
// Perform the AI's turn:
try {
performAISequence();
} catch (InterruptedException e) {
e.printStackTrace();
}
// Used to handle touch input
addListener(new ClickListener() {
@Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
sound.play(0.4f);
if (XimonButton.this.isVisible()) XimonButton.this.setVisible(false);
// System.out.println("Current player list contains:");
// for (int i=0; i < playerList.size(); i++) {
// System.out.println(playerList.get(i));
// }
return true;
}
@Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
// Stop the sound and "turn off" the button light
sound.stop();
XimonButton.this.setVisible(true);
// Count the number of touches for this turn
XimonGame.buttonSequencer.incrementCount();
System.out.println("Count is: " + XimonGame.buttonSequencer.getCount());
System.out.println("Color code is: " + colorCode);
// Append the colorCode to the playerList.
// Loop through the computer list to check if current touch matches
// the computer's num for the index in the ArrayList.
XimonGame.playerList.add(colorCode);
System.out.println("Player list is: " + XimonGame.playerList.toString() + "\n");
//
}
});
}
public void performAISequence () throws InterruptedException {
setTouchable(Touchable.disabled);
for (int i = 0; i < XimonGame.computerList.size(); i++) {
if (code == XimonGame.computerList.get(i)) {
sound.play(0.4f);
XimonButton.this.setVisible(false);
Thread.sleep(XimonGame.buttonSequencer.getRandomInt(400,600));
sound.stop();
XimonButton.this.setVisible(true);
Thread.sleep(XimonGame.buttonSequencer.getRandomInt(100,150));
}
}
setTouchable(Touchable.enabled);
}
@Override
public void draw (Batch batch, float parentAlpha) {
batch.draw(sprite, this.getX(), this.getY(), this.getWidth(), this.getHeight());
}
@Override
public void act(float delta) {
super.act(delta);
}
}
另外:
import java.util.ArrayList;
import java.util.Random;
public class ButtonSequencer {
public Random rand;
private int turn = 0;
private int count;
// public static void addPlayerNums(int code) {
// XimonGame.playerList.add(code);
// }
public int getCount() {
return count;
}
public void incrementCount() {
count++;
}
public void appendRandomNumber(int min, int max, ArrayList<Integer> computerList) {
rand = new Random();
int random = rand.nextInt((max - min) + 1) + min;
computerList.add(random);
turn++;
}
public int getRandomInt(int min, int max) {
rand = new Random();
return rand.nextInt((max - min) + 1) + min;
}
}
很抱歉粘贴了这么多代码,但如果有什么问题,我不知道它在哪里或做什么,所以我认为分享大部分代码是个好主意。
代码太多,我无法对所有内容进行评论,但我看到的最大明显错误是 Thread.sleep
的使用。这将导致您的游戏冻结,直到线程完成休眠。
Libgdx 不是为多线程构建的,因此您需要考虑在计时器上设置内容。需要一段时间的事件需要有在渲染循环的每个 运行 上递减的计时器,并在它们的计时器 运行 关闭时触发它们的事件。
由于您使用的是 stage2d,这变得非常容易,因为 Actions 有自己的计时器,您可以按顺序将它们链接在一起。继续阅读 stage2d's Actions system。您可以创建自己的动作并将它们与中间带有 DelayActions 的 SequenceAction 链接在一起。
举个例子。我不太熟悉你的整个游戏结构,所以我不确定这是否适用于你的代码,但它应该让你对如何做到这一点有一个基本的了解。
public void performAISequence (){
//Get a sequence action from the pool (to avoid unnecessary allocation)
//and give it the first action
SequenceAction sequence = Actions.sequence(Actions.touchable(Touchable.disabled));
for (int i = 0; i < XimonGame.computerList.size(); i++) {
if (code == XimonGame.computerList.get(i)) {
sequence.addAction(Actions.runnable(playSoundRunnable));
sequence.addAction(Actions.hide());
}
//Want to delay it for every item in list, because it may need to wait for other buttons
sequence.addAction(Actions.delay(BUTTON_DELAY_TIME));
if (code == XimonGame.computerList.get(i)) {
sequence.addAction(Actions.show());
sequence.addAction(Actions.runnable(stopSoundRunnable));
}
sequence.addAction(Actions.touchable(Touchable.enabled));
}
this.addAction(sequence);
}
Runnable playSoundRunnable = new Runnable(){
public void run(){
sound.play(0.4f);
}
};
Runnable stopSoundRunnable = new Runnable(){
public void run(){
sound.stop();
}
};