|
|||||||
|
|
|
![]() |
|
|
Strumenti |
|
|
#1 |
|
Senior Member
Iscritto dal: Sep 2004
Messaggi: 404
|
[J2ME] come muovere uno sprite da solo??
Salve a tutti, avrei un problemino da risolvere: sto realizzando un giochino di atletica per l'università e dovrei far gareggiare due atleti, uno comandato dall'utente (e qui non ci sono problemi
Ultimissima cosa: come faccio a determinare chi arriva prima al traguardo? è sufficiente contare la distanza percorsa e confrontarla con la posizione del traguardo?? Grazie a tutti. Ciao |
|
|
|
|
|
#2 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
E' un po' strano perchè se muovi già il tuo omino non dovrebbe esserti difficile far muovere l'altro dal cellulare.
Più o meno dovresti avere una cosa così: Player humanPlayer = ...un omino Player aiPlayer = ...un altro omino Codice:
...
ciclo del motore di gioco
while(gameLoop) {
aggiorna lo stato dell'omino controllato dal giocatore
aggiorna lo stato dell'omino controllato dall'intelligenza artificiale
verifica le condizioni di vittoria
proiezione a video
}
Nel caso del giocatore-umano i suoi spostamenti sono controllati dalla sedicente intelligenza naturale che sta davanti a un cellulare, nel caso del giocatore-cpu il controllo è della non meno sedicente intelligenza artificiale che si risolve in un cumulo di algoritmi che, considerato lo stato del giocatore, quello di ciò che lo circonda, un insieme di obiettivi predefiniti e un certo grado di casualità, comanda l'omino in un senso o nell'altro. Se vuoi farlo semplicemente correre in avanti allora l'AI non farà altro che dire all'omino "continua a correre" ogni volta che debba decidere cosa fare. |
|
|
|
|
|
#3 |
|
Senior Member
Iscritto dal: Sep 2004
Messaggi: 404
|
Ciao e grazie per la risposta (sembra che ci sei solo tu che risp su questo forum
Ho letto quello che mi hai scritto e ho fatto una ricerca su internet riguardo l'intelligenza artificiale, ma ho trovato solo uno sprite che si muove in corrispondenza di quello comandato dall'utente. Io il movimento dell'omino-utente l'ho creato così: //Movimento a destra dell'omino-utente if ((keyStates&RIGHT_PRESSED)!=0) { moving=true; if (currentX<width-15) currentX=Math.min(width,currentX+step2); //Scorro lateralmente nella view window else if (vwx<bgWidth-width) vwx=vwx+step2; if (vwx>bgWidth-width) vwx=bgWidth-width; //Imposto la sequenza right sprite.setFrameSequence(right); } Cosa dovrei scrivere per l'omino-cpu? E come gli assegno la velocità random? Grazie e ciao. |
|
|
|
|
|
#4 |
|
Senior Member
Iscritto dal: Sep 2004
Messaggi: 404
|
Nessuno sa aiutarmi??
|
|
|
|
|
|
#5 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
"Pensiamo facile"
Ti mostro uno schema che ho buttato giù per l'occasione. In teoria dovrebbe aiutarti a intravedere questa fantomatica intelligenza artificiale. L'ho scritto per la piattaforma SE ma è facilmente portabile alla ME. Comunque è solo per dare un'idea del modello. Cos'è un'intelligenza artificiale? Io direi che è un'entità che prende decisioni. Per predere decisioni ha bisogno di informazioni e il tipo di informazioni di cui ha necessità dipende dalle decisioni che deve prendere. Il serpente che si morde la coda, insomma. Limitiamoci al caso "intelligenza artificiale che controlla un'entità in un mondo virtuale". Il minimo sindacale è che l'AI conosca l'entità che deve controllare e il mondo in cui questa si trovi. L'intelligenza artificiale prende decisioni sulla base dello stato del mondo e dell'entità che controlla in un certo istante. Codice:
package game;
/** Intelligenza artificiale */
public interface AI {
/** Assegna a questo esemplare di intelligenza
artificiale il controllo sull'entità e nel
mondo w */
void setEntityInWorld(Entity e, World w);
/** Cogito ergo sum! */
void think();
}
Le "scariche elettriche" sono generate dal motore di gioco. AI tira in ballo delle entità (Entity) e un mondo (World). Definire un'entità potrebbe essere piuttosto complicato, dipende da quando variegato è l'universo che stai creando. Quella che segue è veramente ignominiosa. Codice:
package game;
/** Tipo delle entità */
public interface Entity {
/** Restituisce la posizione dell'entità sull'asse x,
idealmente rispetto all'origine degli assi del mondo
a cui appartiene */
int getX();
/** Restituisce la posizione dell'entità sull'asse y,
idealmente...come getX() */
int getY();
/** Assegna all'entità una posizione sull'asse X */
void setX(int x);
/** Assegna all'entità una posizione sull'asse Y */
void setY(int y);
/** Restituisce l'immagine che rappresenta
questa entità */
java.awt.Image getImage();
/** Imposta l'immagine che rappresenta questa entità */
void setImage(java.awt.Image image);
}
Codice:
package game;
/** Un mondo è un insieme di entità, intelligenze
artificiali e trigger che possiede una linea
temporale ed è in grado di proiettare il suo
contenuto in una pipeline (quest'ultima è una
soluzione compromissoria) */
public interface World {
/** Che ora è in questo mondo? In millisecondi. */
long getCurrentTime();
/** Restituisci i limiti del mondo (che è piatto) */
java.awt.Rectangle getBounds();
/** Restituisce l'elenco delle entità contenute in
questo mondo. Uso un array per compatibilità
con la piattaforma ME, nella SE un Collection
sarebbe preferibile */
Entity[] getEntities();
/** Restituisce il trigger associato all'identificatore
trigger id */
Trigger getTrigger(int triggerId);
/** Restituisce l'insieme di intelligenze artificiali
esistenti in questo mondo */
AI[] getAIs();
/** Aggiorna il mondo. dTime esprime in millisecondi
il tempo trascorso dall'ultimo aggiornamento */
void update(long dTime);
/** Proietta il contenuto di questo mondo usando
la Pipeline in argomento. In verità un proiettore
separato che data una Pipeline ed un World disegni
il contenuto di World in Pipeline sarebbe più
affascinante ma mi risultava meno intuitivo. */
void render(Pipeline p);
}
Codice:
package game;
/** Evento attivabile */
public interface Trigger {
/** true se l'area dell'evento contenga il punto
in argomento */
boolean contains(int x, int y);
/** true se l'evento sia attivo */
boolean isActive();
/** imposta l'attività dell'evento */
void setActive(boolean active);
}
Pensa a World ed Entity come l'insieme di informazioni di cui dispone un AI per decidere cosa fare. Con Entity sa a chi applicare il risultato delle sue decisioni. Con World sa in che stato si trovi il mondo nel momendo in cui deve prendere una decisione: sa "che ora è", sa quali altre entità esistono, sa quali eventi siano attivabili o attivati o disattivati. La Pipeline è un insieme di operazioni che devono essere realizzabili affinchè il gioco possa produrre un qualche risultato grafico. In questo esempio si tratta di un insieme minimo. Codice:
package game;
public interface Pipeline {
void init();
void flush();
void drawImage(java.awt.Image i, int x, int y);
void fillArea(java.awt.Color c, java.awt.Rectangle r);
}
Questa è la AI che controlla il giocatore del cellulare. E' incompleta perchè corre senza fermarsi e produce unicamente un'animazione per spostamento (manca l'animazione per sostituzione delle immagini) ma dovrebbe proporre qualche spunto. Contiene una variazione casuale della velocità. Senza float, com'è necessario per alcune configurazioni della piattaforma ME. Codice:
package game;
import java.util.*;
public class CPUController implements AI {
private World world;
private Entity entity;
private long timeLine = -1;
private long dTime;
//ogni quanti millisecondi aumenta di 1 pixel la posizione del giocatore?
private long upTime = 10;
//ogni quanti millisecondi tira a caso una velocità?
private long speedChangeTick = 1000;
//quanto tempo è trascorso dall'ultimo "cambio di marcia"?
private long speedChangeDTime = 0;
private Random random = new Random();
private Trigger starter;
public void setEntityInWorld(Entity entity, World world) {
this.entity = entity;
this.world = world;
starter = world.getTrigger(GameConstants.STARTER);
}
public void think() {
long time = world.getCurrentTime();
//Può correre? Sì se lo starter abbia dato il via
if(starter != null && starter.isActive()) {
//calcola il tempo trascorso dall'ultima "scossa"
dTime += time - timeLine;
/*aumenta la linea temporale usata per determinare
quanto tempo è trascorso dall'ultimo cambio di velocità */
speedChangeDTime += dTime;
while(speedChangeDTime > speedChangeTick) {
upTime = 5 + random.nextInt(10);
speedChangeDTime -= speedChangeTick;
System.out.println(upTime);
}
/* senza float, anzichè calcolare la quantità di spazio
da percorre come prodotto della velocità per il tempo,
aumentiamo di un pixel la posizione per un numero di volte
pari a quanti cicli di spostamento possono essere realizzati
nel tempo trascorso dall'ultimo spostamento */
while(dTime >= upTime) {
dTime -= upTime;
entity.setX(entity.getX() + 1);
}
} else {
dTime = 0;
}
timeLine = time;
}
}
Codice:
package game;
public class Starter implements Trigger {
private boolean active;
public void setActive(boolean active) {
this.active = active;
}
public boolean contains(int x, int y) {
return true;
}
public boolean isActive() {
return active;
}
}
Codice:
package game;
public class StarterController implements AI {
private World world;
private long elapsedTime = 0;
private long time0;
public void setEntityInWorld(Entity entity, World world) {
this.world = world;
}
public void think() {
Trigger starter = world.getTrigger(GameConstants.STARTER);
if(starter != null && starter.isActive() == false) {
if(time0 == 0) {
time0 = world.getCurrentTime();
} else {
long time1 = world.getCurrentTime();
elapsedTime += time1 - time0;
time0 = time1;
}
if(elapsedTime > 5000) {
/* Se sono passati più di cinque secondi da quando
questa intelligenza artificiale ha iniziato "a pensare",
allora si aprono le danze...*/
System.out.println("VIA!!!");
starter.setActive(true);
}
}
}
}
Poi c'è l'intelligenza artificiale del corridore controllato dal giocatore umano. E che c'entra? A naso niente però m'è venuta così. C'è sempre un effetto prodotto da una scelta fondata su delle informazioni. La differenza è che a chi "sceglie" per il corridore umano non basta sapere chi sia l'entità da controllare e quale sia lo stato del mondo in cui questa si trova: servono anche i comandi che l'utente vorrebbe impartire. Che questi comandi abbiano o non abbiano effetto dipende poi da un insieme di condizioni. Spara. E se non ho più munizioni? Ciccia. Lo stato dei comandi impartiti dal giocatore è conservato in un oggetto di tipo KeyManager. Codice:
package game;
public interface KeyManager {
int UP = 0;
int DOWN = 1;
int LEFT = 2;
int RIGHT = 3;
boolean isKeyPressed(int keyCode);
}
L'insieme di algoritmi (uno Codice:
package game;
public class HumanController implements AI {
private KeyManager keyManager;
private World world;
private Entity entity;
private long timeLine = -1;
private long dTime;
//ogni quanti millisecondi aumenta di 1 pixel la posizione del giocatore?
private long upTime = 10;
private Trigger starter;
public HumanController(KeyManager keyManager) {
this.keyManager = keyManager;
}
public void setEntityInWorld(Entity entity, World world) {
this.entity = entity;
this.world = world;
starter = world.getTrigger(GameConstants.STARTER);
}
public void think() {
long time = world.getCurrentTime();
if(keyManager.isKeyPressed(KeyManager.RIGHT) && world.getTrigger(GameConstants.STARTER).isActive()) {
dTime += time - timeLine;
run();
} else {
dTime = 0;
}
timeLine = time;
}
private void run() {
while(dTime >= upTime) {
dTime -= upTime;
entity.setX(entity.getX() + 1);
}
}
}
Come l'AI del corridore artificiale inizia a correre solo quando lo starter è "true", così l'AI del corridore umano consente a questi di partire solo quando lo stesso starter sia true. Il metodo run computa lo spostamento nello stesso modo di CPUController per via dell'assenza dei numeri in virgola mobile sulla piattaforma ME. Siamo alle battute finali. Questa è una versione concreta del concetto di entità (Entity) che è un mero insieme di attributi. Codice:
package game;
import java.awt.Image;
public class Sprite implements Entity {
private int x, y;
private Image image;
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public Image getImage() {
return image;
}
public void setImage(Image i) {
this.image = i;
}
}
Il motore di gioco è un ciclo periodico che segnala al mondo (World) che è giunto il momento di aggiornarsi e proietta quel mondo attraverso una pipeline. Codice:
package game;
import java.awt.*;
public class GameEngine implements Runnable {
private volatile boolean loop;
private Thread runner;
private volatile World world;
private Pipeline pipe;
public GameEngine(World w, Pipeline p) {
this.world = w;
this.pipe = p;
}
public void start() {
if(!loop) {
loop = true;
runner = new Thread(this, "Game engine");
runner.start();
}
}
public void stop() {
loop = false;
}
public void run() {
long time0 = System.currentTimeMillis();
long dTime, time1;
while(loop) {
time1 = System.currentTimeMillis();
dTime = time1 - time0;
time0 = time1;
world.update(dTime);
world.render(pipe);
try {
Thread.sleep(10);
} catch(InterruptedException ex) {
loop = false;
return;
}
}
}
}
Quello che segue è lo schermo di gioco che concretizza Pipeline e KeyManager. Qui uso Canvas e BufferStrategy della piattaforma SE che si comportano esattamente come GameCanvas nella piattaforma ME. Anzi, GameCanvas è pure più semplice perchè integra di suo il procedimento per la gestione dello stato della tastiera. Codice:
package game;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
public class GameScreen extends Canvas implements Pipeline, KeyManager, KeyListener {
private volatile Graphics drawGraphics;
private volatile boolean[] keyState = new boolean[4];
public GameScreen(int width, int height) {
setPreferredSize(new Dimension(width, height));
setSize(new Dimension(width, height));
addKeyListener(this);
setFocusable(true);
}
public void addNotify() {
super.addNotify();
createBufferStrategy(2);
}
public void init() {
drawGraphics = getBufferStrategy().getDrawGraphics();
}
public void flush() {
getBufferStrategy().show();
}
public void fillArea(Color c, Rectangle r) {
drawGraphics.setColor(c);
drawGraphics.fillRect(r.x, r.y, r.width, r.height);
}
public void drawImage(Image image, int x, int y) {
drawGraphics.drawImage(image, x, y, null);
}
public boolean isKeyPressed(int keyCode) {
return keyState[keyCode];
}
public void keyPressed(KeyEvent e) {
setKeyState(e.getKeyCode(), true);
}
public void keyReleased(KeyEvent e) {
setKeyState(e.getKeyCode(), false);
}
public void keyTyped(KeyEvent e) {}
private void setKeyState(int keyEventCode, boolean state) {
switch(keyEventCode) {
case KeyEvent.VK_UP:
keyState[UP] = state;
break;
case KeyEvent.VK_DOWN:
keyState[DOWN] = state;
break;
case KeyEvent.VK_LEFT:
keyState[left] = state;
break;
case KeyEvent.VK_RIGHT:
keyState[right] = state;
break;
}
}
}
Poi arriva BasicWorld, che è uno World concreto. Una lista di AI, Entity e Trigger che proietta le entità su una pipeline (render) e aggiorna periodicamente tutte le AI contenute (update). Codice:
package game;
import java.awt.*;
public class BasicWorld implements World {
private Rectangle bounds;
private Entity[] entities;
private Trigger[] triggers;
private AI[] ais;
private long timeLine = 0;
public BasicWorld(Entity[] entities, AI[] ais, Trigger[] triggers, Rectangle bounds) {
this.entities = entities;
this.ais = ais;
this.bounds = bounds;
this.triggers = triggers;
}
public Trigger getTrigger(int triggerId) {
return
triggerId >= 0 && triggerId < triggers.length ?
triggers[triggerId] :
null;
}
public Entity[] getEntities() {
return entities;
}
public AI[] getAIs() {
return ais;
}
public long getCurrentTime() {
return timeLine;
}
public void update(long dTime) {
timeLine += dTime;
for(int i = 0; i < ais.length; i++) {
ais[i].think();
}
}
public void render(Pipeline p) {
p.init();
p.fillArea(Color.BLACK, getBounds());
for(int i = 0; i < entities.length; i++) {
Entity e = entities[i];
p.drawImage(e.getImage(), e.getX(), e.getY());
}
p.flush();
}
public Rectangle getBounds() {
return bounds;
}
}
Codice:
package game;
import java.awt.*;
import javax.swing.*;
import javax.imageio.*;
import java.awt.event.*;
public class Main implements Runnable {
public static void main(String[] args) {
Main main = new Main();
try {
main.init();
} catch(Exception ex) {
throw new RuntimeException(ex);
}
SwingUtilities.invokeLater(main);
}
private volatile GameEngine engine;
private volatile Component screen;
private Window window;
public void init() throws Exception {
GameScreen gameScreen = new GameScreen(300, 300);
/* Crea l'AI di supporto al giocatore, che richiede un KeyManager
in costruzione: il KeyManager l'abbiamo qui sopra (GameScreen) */
HumanController humanController = new HumanController(gameScreen);
/* Crea l'AI del corridore artificiale */
CPUController cpuController = new CPUController(); //ai cpu
/* Crea l'AI che controlla il Trigger che avvia la corsa */
StarterController starterController = new StarterController();
/* Carica due immagine tanto per muovere qualcosa sullo schermo... */
Image humanImage = ImageIO.read(getClass().getResource("/human.png"));
Image cpuImage = ImageIO.read(getClass().getResource("/cpu.png"));
/* Crea l'entità (Sprite) del corridore umano e artificiale*/
Sprite humanRunner = new Sprite();
Sprite cpuRunner = new Sprite();
/* e gli rifila le due immagini su caricate */
humanRunner.setImage(humanImage);
cpuRunner.setImage(cpuImage);
/* sposta il corridore artificiale un po' più sotto
(il valore Y predefinito è zero) */
cpuRunner.setY(cpuImage.getWidth(null) + 10);
/* Crea la lista di entità da inserire nel mondo */
Entity[] entities = { humanRunner, cpuRunner };
/* Crea la lista delle intelligenze artificiali da
applicare al mondo */
AI[] ais = { humanController, cpuController, starterController };
/* Crea la lista di eventi attivabili appartenenti al
mondo */
Trigger[] triggers = { starter };
/* Crea un'area per il mondo (piatto) */
Rectangle bounds = new Rectangle(0, 0, 300, 300);
/* Crea un mondo con le entità, AI, eventi e limiti su prodotti */
BasicWorld world = new BasicWorld(entities, ais, triggers, bounds);
/* Affida alla AI di supporto al giocatore l'entità che
rappresenta il suo corridore ed il mondo in cui questa esiste */
humanController.setEntityInWorld(humanRunner, world);
/* Affida alla AI che controlla il giocatore artificiale
l'entità del corridore che dovrà comandare ed il mondo
in cui questa esiste */
cpuController.setEntityInWorld(cpuRunner, world);
/* Affida alla AI che controlla il Trigger starter il mondo...
e basta. A differenza delle altre, questa AI non opera
su un'entità. Probabilmente nessuna AI dovrebbe ricevere
esplicitamente un'entità su cui operare perchè questa informazioni
è ricavabile dal mondo (che contiene tutte le entità). Materia per
un'altra discussione =)*/
starterController.setEntityInWorld(null, world);
/* Crea il motore di gioco */
GameEngine engine = new GameEngine(world, gameScreen);
/* nel metodo run il component screen sarà
aggiunto ad un frame e il motore avviato.
this.engine e screen servono per condividere tra i due
metodi (init e main) engine e gameScreen */
this.engine = engine;
screen = gameScreen;
}
public void run() {
Frame frame = new Frame("Game sample");
window = frame;
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
shutdown();
}
});
frame.add(screen);
frame.pack();
frame.setVisible(true);
//parte tutto
engine.start();
}
/* invocato alla chiusura della finestra */
private void shutdown() {
engine.stop();
window.dispose();
}
}
|
|
|
|
|
|
#6 |
|
Senior Member
Iscritto dal: Dec 2002
Messaggi: 3359
|
Ma quello comandato dal giocatore come si deve muovere?
Cioè per esempio io premo RIGHT e l'omino si sposta a destra a velocità costante? Comunque devi fare la stessa identica cosa che faresti per quello del giocatore...nn vedo il rpoblema |
|
|
|
|
|
#7 |
|
Senior Member
Iscritto dal: Sep 2004
Messaggi: 404
|
Grazie per la risposta
Domani gli do un'occhiata, adesso non sono lucido per capire ciò che hai scritto Ciao |
|
|
|
|
|
#8 |
|
Senior Member
Iscritto dal: Sep 2004
Messaggi: 404
|
Salve ragazzi, grazie per l'aiuto che mi avete dato..
Sono riuscito a far muovere l'omino-cpu da solo assegnandogli una velocità random. Adesso però ho un altro problema: in pratica quando scorro nella view verso destra, appena l'omino arriva alla fine del display, non va più avanti finchè quello comadato dall'utente non lo raggiunge. Però quando lo raggiunge sembra che procedono come treni, cioè non sono più claudicanti e inoltre non mi viene nemmeno verificata la condizione che mi permette di determinare il vincitore. Come dovrei fare? Per verificare il vincitore ho scritto questo: //Verifico chi arriva per primo al traguardo e blocco l'applicazione if((enemy.getRefPixelX()==100)||(sprite.getRefPixelX()==100)) //modificare il parametro 100 { stopGame(); } Poi nel metodo stopGame() ho il controllo per determinare chi vince (funziona, l'ho provato Spero di essere stato chiaro... Ancora grazie e ciao. |
|
|
|
|
|
#9 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Dipende da cosa hai scritto per fargli fare quello che avresti voluto. Siccome non lo fanno, i casi sono due: o ai creato un'intelligenza artificiale tipo HAL9000 che a un certo punto fa quello che gli pare, o il codice che hai scritto non rappresenta esattamente le tue intenzioni.
Nel primo caso ti spetta di diritto il premio Turing. Nel secondo...benvenuto nel solito tran tran da programmatore Ad esempio, affinchè l'if che hai incollato funzioni l'ascissa del pixel di riferimento di uno dei due sprite deve avere valore 100. Esaminando l'algoritmo per lo spostamento che hai adottato, è certo i due sprite si trovino, prima o poi, in posizione tale che si verifichi la condizione getRefPixelX() == 100? Magari si, magari no, dipende da come applichi la velocità. Se, una volta giunti alla "barriera" rappresentata dalla fine del display, i due sprite procedono appaiati, nonostante velocità diverse, è possibile – vado a naso, e col raffreddore – che lo spostamento dell'area di proiezione sia collegato alla velocità del più lento dei due Sprite. In questo caso lo sprite più rapido si troverebbe a "sbattere" contro la barriera ed attendere l'arrivo dello sprite più lento ad ogni aggiornamento. Ma è solo una blanda teoria, non avendo per le mani l'algoritmo con cui realizzi lo spostamento. |
|
|
|
|
| Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 14:50.



















