Come creare un gioco senza OOP? [chiuso]


10

Attualmente sto studiando lo sviluppo del gioco e praticando la creazione di giochi.

Uso molto OOP nei miei giochi. Ad esempio, ogni missile che viene sparato è un'istanza di un Missileoggetto e aggiunto a un elenco di Missileoggetti. Ogni carro armato nel gioco è un Tankoggetto. Eccetera.

L'intero progetto del programma si basa su questo. Ad esempio, avere un elenco di Missileoggetti mi consente a ogni frame di spostare i missili, disegnarli, ecc. E avere un'istanza di un Tankoggetto per ogni serbatoio mi permette di controllare ogni serbatoio se si scontra con qualcosa, ecc.

È difficile per me immaginare come un gioco (che è più complesso di Pac-Man) possa essere programmato in un linguaggio non OO. (Ovviamente senza rispetto per i programmatori non OO). Non solo in termini di quanto tempo ci vorrà, ma soprattutto in termini di come un gioco potrebbe essere progettato in questo modo.

Non riesco a immaginare di progettare un gioco senza usare la programmazione orientata agli oggetti, perché la mia intera comprensione di come progettare un programma di gioco si basa su OOP.

Vorrei chiedere: oggi ci sono giochi che non sono programmati usando OOP, in modo simile a quello che ho descritto sopra? Esistono giochi "professionali" che non utilizzano OOP come fattore principale nel processo di sviluppo?

Se è così, potresti darmi un'idea di come, ad esempio, il rilevamento delle collisioni tra un carro armato e N numero di missili potrebbe essere implementato, senza OOP?


6
È una domanda filosofica? Anche se non chiami "oggetti" i tuoi carri armati, probabilmente vorrai "entità", "attori", "agenti", "strutture" o solo qualche altro nome per la stessa idea, che è una raccolta di attributi e comportamenti che compongono una cosa cuboide rotante con una torretta che può sparare cose, chiamato un carro armato. I linguaggi di programmazione avranno diversi modi di formalizzare questa stessa idea, ma alla fine sarà un carro armato.
Anko,

Molti giochi utilizzano un sistema basato su componenti poiché questa risposta descrive: gamedev.stackexchange.com/a/31491/9366
John McDonald

Questo è sia estremamente ampio (che potresti eventualmente correggere restringendo l'ambito) sia non proprio specifico per lo sviluppo del gioco (poiché la creazione di software senza tecniche OO non è qualcosa che uno sviluppatore di giochi ti darebbe una risposta migliore di qualsiasi altro sviluppatore di software), il che rende fuori tema qui, temo.

Potrebbe essere adatto a StackOverflow oppure puoi consultare il centro assistenza per trovare una selezione di siti specifici per lo sviluppo di giochi (come GDNet) che consentirebbero questo tipo di argomento ampio e orientato alla discussione. In bocca al lupo!

Risposte:


16

Non riesco a immaginare di progettare un gioco senza usare la programmazione orientata agli oggetti, perché la mia intera comprensione di come progettare un programma di gioco si basa su OOP.

Quindi sarà probabilmente utile provare a scrivere alcuni programmi in stile non OO. Anche se scopri che questo non è pragmatico per te, probabilmente imparerai molto lungo la strada che ti aiuterà in futuro.

Lo stile OO è piuttosto adatto per i giochi perché i giochi riguardano quasi sempre la manipolazione di oggetti con stato. Un raggio laser colpisce il robot e lo stato del robot cambia mentre la sua identità rimane la stessa.

Tuttavia è possibile programmare i giochi in uno stile funzionale . In uno stile funzionale, lo stato non cambia di per sé. Gli oggetti sono immutabili. Invece di cambiare gli oggetti, fai la domanda in che modo l'universo sarebbe diverso se lo cambiassi? e quindi produrre un intero nuovo universo che ha la proprietà modificata. Naturalmente, puoi riutilizzare molto dell'universo precedentemente esistente perché è immutabile .

Nella programmazione funzionale ogni funzione deve calcolare il suo valore di ritorno esclusivamente dalle informazioni trasmesse; non si legge da "stato globale".

Se lo fai, il problema fondamentale che dovrai affrontare è che ogni aggiornamento non è distruttivo . Quando il laser colpisce il robot non si modifica lo stato del robot. Alla fine si calcola un universo completamente nuovo identico al vecchio universo, tranne per il fatto che il robot ha uno stato diverso; se hai bisogno di quel vecchio universo, è ancora lì, invariato.

Questa serie di articoli di blog ha più pensieri sulla scrittura di giochi in uno stile funzionale:

http://prog21.dadgum.com/23.html

Questo articolo affronta in modo specifico la domanda "la shell colpisce un carro armato":

http://prog21.dadgum.com/189.html

In effetti, basta leggere l'intero blog. C'è roba buona lì e gli articoli sono brevi.


12

Qualsiasi programma orientato agli oggetti può essere rifattorizzato in un programma procedurale sostituendo tutte le classi con strutture e convertendo tutte le funzioni membro in funzioni autonome che prendono l'oggetto che sarebbe thiscome argomento.

Così

 missile.setVelocity(100);

diventa

 setMissileVelocity(missile, 100);

o quando quella funzione è banale, lo fai e basta

 missile.velocity = 100;

La differenza principale tra programmazione orientata agli oggetti e programmazione procedurale è il modo in cui trattate i vostri dati. In OOP, i dati sono intelligenti . Gestisce e manipola se stesso. Ma nella programmazione procedurale, i dati sono stupidi . Non fa nulla da solo e deve essere manipolato dall'esterno.

Quando consideri le strutture troppo orientate agli oggetti, puoi sostituire una matrice di strutture con più array, una per tutto ciò che sarebbe una variabile di un missile. Così

struct Missile {
     int x;
     int y;
     int velocity;
}

Missile missiles[256];

diventa

int missileX[256];
int missileY[256];
int missileVelocities[256];

In questo progetto, una funzione che esegue un'operazione che coinvolge più attributi sullo stesso missile ora prenderebbe un indice di array anziché un riferimento a una struttura. La sua implementazione sarebbe simile a questa:

function updateMissilePosition(int index) {
     missileX[index] += missileVelocity[index];
}

1
Ma missileè un'istanza di un oggetto. In non OOP, non ci sono casi, sono corretto? In tal caso, come è possibile impostare setMissileVelocity (missile, 100)?
user3150201

1
@ user3150201 Non sei del tutto corretto. La maggior parte dei linguaggi non-OOP (e direi qualsiasi di quelli adatti allo sviluppo di giochi seri) supporta le strutture. Una struttura è un po 'come una classe, solo che non contiene nulla tranne le variabili pubbliche. Quindi sarebbe permetterà di creare un tipo Missile, che è una struttura con diversi campi, come x, y, anglee velocity.
Philipp,

@ user3150201 Risposta aggiornata con una sezione su come farlo senza strutture.
Philipp,

Bella risposta Philipp, anche se non capisco perché non si voglia programmare Object-Oriented. È davvero difficile leggere lingue non-OOP e può essere frustrante. Il codice può diventare un disastro in pochissimo tempo.
Zhafur,

2
@Zhafur Sei consapevole che la tua affermazione è un'enorme esca di fiamma, vero?
Philipp,

6

Lo faccio come segue:

  • Tutte le classi / i metodi OOP hanno accesso a this. Al fine di utilizzare thisin un approccio non OO, passare semplicemente in qualsiasi istanza (vedere il punto successivo) thisdovrebbe essere, come primo parametro.
  • Ora, come per esempio, puoi passare structs nelle tue funzioni come this, ma trovo che il modo migliore per ottenere buone prestazioni della cache per oggetti prolifici, come entità o particelle, sia semplicemente passare un singolo indice in più matrici di primitive o piccole structs. Quindi questo indice viene utilizzato per ogni singolo membro di dati della classe originale. Quindi, per esempio, se lo avessi fatto

...

class Entity //let's say you had 100 instances of this
{
   int a;
   char b;
   function foo() 
   {
      .../*can access 'this' herein*/
   }
}

Lo sostituiresti con

int a[100];
char b[100];
function foo(int index);

In modo che ora passi un indice nella funzione per ottenere ciò che normalmente sarebbe this.

Tieni presente che potresti voler utilizzare matrici di primitive come sopra o matrici di structs, a seconda del modo migliore di intercalare i tuoi dati per una buona localizzazione della cache (località di riferimento). Naturalmente, le prestazioni della cache decenti si basano su molto più di questo - in particolare, su quale lingua / piattaforma stai scrivendo il tuo codice - ma anche in linguaggi basati su VM, allocati dinamicamente come Java, tendono a disporre di grandi array lineari di primitive mostra caratteristiche di prestazione migliori rispetto alle istanze di oggetti. Il motivo principale di ciò è che gli oggetti sono accessibili per riferimento e questo significa che si sta saltando tutta la memoria per accedere ai dati - inefficiente rispetto all'accesso contiguo in modo contiguo da un array di grandi dimensioni.

Per ulteriori informazioni sulla costruzione di entità, ecc. Come array di strutture o primitive, vedere Evolve la tua gerarchia di Mick West .


0

Oltre alle risposte esistenti, potresti voler sapere come fare il polimorfismo in un linguaggio procedurale.

Esistono due approcci:

Memorizzare il tipo

In questo caso la struttura ha un campo per l'identificatore di tipo, probabilmente un enum, che viene verificato usando switchun'istruzione quando è necessario eseguire un'azione specifica per tipo.

L'altro modo è:

Memorizzare i puntatori a funzione

Non hai menzionato in quale linguaggio di programmazione hai esperienza, ma in vari linguaggi sono anche chiamati callback, delegati, eventi o funzioni di alto ordine o semplicemente oggetti funzione.

In questo caso non memorizzerete un tipo, ma memorizzerete un puntatore / riferimento a una funzione che esegue l'azione specifica. Quando è necessario eseguire un'azione specifica per tipo, è sufficiente chiamare questa funzione. Questo assomiglia molto ai normali metodi virtuali.

Consentendo di impostare ciascuna funzione in modo indipendente, si ottiene il modello di strategia libero.


Per quanto riguarda il tuo ultimo paragrafo con il rilevamento delle collisioni. Penso che tu abbia probabilmente più tipi di carri armati e tu abbia più tipi di missili e ogni combinazione può potenzialmente avere un risultato diverso quando si scontrano. Se questo è ciò che cerchi, hai un problema che non è stato ancora risolto nemmeno dai linguaggi OOP: invio multiplo e metodi multipli che possono avere più thisparametri di diversi tipi. Per questo problema ci sono ancora due alternative:

  • Interruttori annidati per ogni combinazione di carro armato e missile.
  • Matrici di invio bidimensionali, che contengono puntatori alle funzioni per ogni combinazione di carri armati e missili.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.