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 :) ) e un altro comandato dal dispositivo (e qui non saprei come fare... :mad: ). Inoltre vorrei assegnare all'atleta comdato dal dispositivo una velocità random in modo tale che ogni tanto vinca anche l'utente... :D
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
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
...
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
}
Tra l'aggiornamento dello stato dell'omino del giocatore e quello della cpu cambia solo la provenienza dell'input.
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.
Ciao e grazie per la risposta (sembra che ci sei solo tu che risp su questo forum :sofico: ).
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.
Nessuno sa aiutarmi?? :mc:
"Pensiamo facile" :D.
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.
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();
}
Un AI "sa" che deve controllare un'entità, "sa" che esiste un mondo virtuale. E' facile immaginare come possa pensare: se la mia "intelligenza" dice che devo spostare sempre l'entità a destra, ebbene farò in modo che la sua posizione nel mondo muti verso destra. Coma fa un AI a pensare? Alla Frankenstein: qualcuno gli da una scarica e questa pensa. Una volta. Più scariche a intervalli regolari producono un flusso di pensiero (che parolone :D).
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.
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);
}
In pratica è un cumulo di attributi: non fa nient'altro che possederli. Non è detto che sia l'opzioni migliore ma ha un certo non so che, che mi ha attirato. A seguire il pianeta virtuale.
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);
}
Un Trigger è un evento attivabile.
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);
}
Trigger m'è venuto in mente pensando alla partenza della corsa. Quand'è che l'omino controllato dalla cpu parte? Be', quando lo starter da il colpo di pistola :D. E vale anche per il corridore umano: non è che possa schizzare come un fulmine appena compare la sua immagine sullo schermo.
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.
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);
}
Con questa mano interamente fatta di due di picche, possiamo pensare all'intelligenza artificiale che controlla un corridore, CPUController. Dopo che un certo evento (lo sparo dello starter) è stato attivato, CPUController inizia a far correre verso X+ (destra) l'entità controllata. Di quando in quando, CPUController tira a caso la velocità di corsa. Alla fine è come correre contro uno psicopatico :D.
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.
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;
}
}
CPUController cerca un Trigger che segnali l'inizio della corsa. Quel Trigger è semplicemente:
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;
}
}
'na roba che è vera o falsa e basta. Quand'è che diventa vera? Lo decide un'altra intelligenza artificiale, StarterController.
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);
}
}
}
}
Questo po' po' di filosofo qui sopra dopo 5 secondi dal suo primo "cogito ergo sum", imposta a true il Trigger STARTER.
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.
package game;
public interface KeyManager {
int UP = 0;
int DOWN = 1;
int LEFT = 2;
int RIGHT = 3;
boolean isKeyPressed(int keyCode);
}
Ampia varietà di comandi :D.
L'insieme di algoritmi (uno :D) che decide come l'entità controllata dal giocatore risponda ai suoi comandi è l'AI HumanController.
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);
}
}
}
Nota bene che ci sono tre intelligenze artificiali e tre modi diversi di contare il tempo: un chiaro caso di personalità multipla :cry:
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.
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;
}
}
Pochi, per giunta.
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.
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;
}
}
}
}
Mi sconfifferebbe di più l'idea di un oggetto di tipo Projector che dato un mondo ed una pipeline disegni il mondo con la pipeline ma m'è venuta fuori così, che ci vuoi fare :D.
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.
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;
}
}
}
La manciata di metodi rilevante è fillArea, init, flush e drawImage di Pipeline e isKeyPressed di KeyManager.
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).
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;
}
}
E finalmente (papparapà) il Main.
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();
}
}
E questo è più o meno tutto. Dopo circa 5 secondi l'omino della cpu schizza a destra e ogni tanto claudica per via del cambio di passo. Nel complesso c'è poco di "giocoso" in quello che fanno le AI proposto ma il modello ha la flessibilità sufficiente a creare i tuoi due corridori.
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
Grazie per la risposta :sofico:
Domani gli do un'occhiata, adesso non sono lucido per capire ciò che hai scritto :p
Ciao
Salve ragazzi, grazie per l'aiuto che mi avete dato.. :sofico:
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 :p , però fino ad una distanza di 100, poi non più, anche passando la lunghezza della pista, come mai?? :confused: )
Spero di essere stato chiaro...
Ancora grazie e ciao.
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 :D.
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.
vBulletin® v3.6.4, Copyright ©2000-2026, Jelsoft Enterprises Ltd.