Skip to content

Commit

Permalink
Added my part of report
Browse files Browse the repository at this point in the history
  • Loading branch information
frabitta authored Feb 18, 2024
1 parent fb40125 commit dc63c88
Showing 1 changed file with 339 additions and 0 deletions.
339 changes: 339 additions & 0 deletions report.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,321 @@ Inoltre, alla sua creazione si occupa di creare un grafo delle GameTile libere,
per permettere loro di inseguire un certo target.

### Bittasi Francesco
#### GraphicsComponent
```mermaid
classDiagram
class GraphicsComponent {
<<interface>>
+getColor(): EngineColor
+getPositions(): List<Position2D>
+setColor(color: EngineColor): void
+setPositions(pos: List<Position2D>): void
+getType(): GraphicType
}
class GraphicType {
<<enum>>
WORLD
HUD
}
class EngineColor {
<<interface>>
+BLUE: EngineColor
+RED: EngineColor
+GREEN: EngineColor
+WHITE: EngineColor
+BLACK: EngineColor
+YELLOW: EngineColor
+getR(): int
+getG(): int
+getB(): int
}
GraphicsComponent --> GraphicType
GraphicsComponent --> EngineColor
class PolygonGraphicsComponent {
<<interface>>
}
PolygonGraphicsComponent --|> GraphicsComponent
class StatusBarGraphicsComponent {
<<interface>>
+setStatusColor(statusColor: EngineColor): void
+setDimension(base: int, height: int): void
+setRange(min : int , max : int) : void
+setStatus(i: int): void
+getStatusColor(): EngineColor
+getBase(): int
+getHeight(): int
+getMax(): int
+getMin(): int
+getStatus(): int
+getPercentage(): double
}
StatusBarGraphicsComponent --|> GraphicsComponent
class TextGraphicsComponent {
<<interface>>
+getText(): String
+setText(text: String): void
+setSize(size: int): void
+getSize(): int
}
TextGraphicsComponent --|> GraphicsComponent
```
**Problema:** Creare oggetti che rappresentino l'aspetto grafico di elementi del modello del gioco, in maniera indipendente dalla tecnologia della View.

**Soluzione:** L'interfaccia GraphicsComponent rappresenta un componente grafico generico, questa può essere specializzao in più tipi: Testo, Poligono, Barra di stato, etc.
- Il model dell'applicazione si occupa di creare e aggiornare questi oggetti per dire che aspetto devono assumere gli elementi del gioco.
- Avendo definito tutte queste proprietà, tra cui i colori e che tipo di elemento grafico sono (se parte del mondo di gioco o della HUD), questi componenti grafici sono totalmente indipendenti dalla tecnologia di View adottata.

```mermaid
classDiagram
class RenderableGraphicComponent {
<<interface>>
+getRenderer(): GraphicsComponentRenderer
}
RenderableGraphicComponent --|> GraphicsComponent
class GraphicsComponentRenderer {
<<interface>>
+render(g: Graphics2D, camera: ViewCamera): void
+setComponent(gComp: GraphicsComponent): void
}
RenderableGraphicComponent *-- GraphicsComponentRenderer
GraphicsComponentRenderer *-- GraphicsComponent
class GraphicsComponent {
<<interface>>
}
```
**Problema:** Permettere alla view di leggere questi componenti grafici e rappresentarli a schermo.

**Soluzione:** Per facilitare la view nell'interpretare questi oggetti è stata definita un'interfaccia che estende GraphicsComponent e che permette di contenere un oggetto che si occupa di renderizzare questi componenti grafici, specifico per la tecnologia usata.
- Il GraphicsComponentRenderer è dunque unico per Swing: è legato ad un solo componente grafico e alla chiamata del metodo `render` lo stampa a schermo avendo il riferimento alla Camera e all'oggetto Graphics2D.
- Per l'implementazione della view alla fine i componenti grafici che le vengono passati si comportano similmente al pattern Command: ottenuto il set di Componenti grafici filtra quelli che è capace di interpretare (chi implementa RenderableGraphicComponent) e li stampa a schermo chiamando il metodo `render`.

```mermaid
classDiagram
class GraphicsComponentsFactory {
<<interface>>
+polygon(color: EngineColor, pos: List<Position2D>, type: GraphicType): PolygonGraphicsComponent
+text(color: EngineColor, pos: Position2D, size: int, text: String, type: GraphicType): TextGraphicsComponent
+statusBar(bgColor: EngineColor, statusColor: EngineColor, pos: Position2D, base: int, height: int, type: GraphicType): StatusBarGraphicsComponent
}
GraphicsComponentsFactory --> TextGraphicsComponent
GraphicsComponentsFactory --> PolygonGraphicsComponent
GraphicsComponentsFactory --> StatusBarGraphicsComponent
GraphicsComponentsFactory --> RenderableGraphicComponent
class RenderableGraphicComponent {
<<interface>>
}
class PolygonGraphicsComponent {
<<interface>>
}
class StatusBarGraphicsComponent {
<<interface>>
}
class TextGraphicsComponent {
<<interface>>
}
```

**Problema:** creare facilmente i componenti grafici.

**Soluzione:** Per permettere al Model dell'applicazione di creare i componenti grafici senza dover sapere dell'associazione di questi coi GraphicsComponentRenderer è stata creata una factory che restituisse il tipo di componente grafico richiesto con già incorporato il renderer della view necessario per permetterne la stampa.

#### Camera
```mermaid
classDiagram
class ViewCamera {
<<interface>>
+getScreenPosition(pos: Position2D, type: GraphicType): Position2D
+getScreenPositions(pos: List<Position2D>, type: GraphicType): List<Position2D>
+setScreenDimension(width: double, height: double): void
+getHoriOffset(): double
+getVertOffset(): double
+getSizeRatio(): double
}
class WorldCamera {
<<interface>>
+moveTo(position: Position2D): void
+getWorldPosition(pos: Position2D): Position2D
+centerOn(position: Position2D): void
+setArea(height: int, width: int): void
+keepInArea(position: Position2D): void
}
class CameraMover {
<<interface>>
+installCamera(camera: WorldCamera): void
}
CameraMover --> WorldCamera
class CameraViewer {
<<interface>>
+installCamera(camera: ViewCamera): void
}
CameraViewer --> ViewCamera
class Camera {
<<interface>>
}
Camera --|> WorldCamera
Camera --|> ViewCamera
CameraImpl --|> Camera
EngineImpl *-- CameraImpl
World --|> CameraMover
World *-- CameraImpl
ViewImpl *-- CameraImpl
ViewImpl --|> CameraViewer
```
**Problema:** Sullo schermo va visualizzata solo una parte del mondo di gioco, deve mantere le proporzioni corrette per qualunque formato di schermo e la porzione visibile deve essere scelta dal Model dell'applicazione.

**Soluzione:** L'engine si occupa di creare un oggetto (nel progetto la classe "CameraImpl") che si occupa di effettuare i calcoli di conversione da coordinate del mondo a coordinate sullo schermo e viceversa. L'engine darà il riferimento a questo oggetto al World e all'EngineView che lo interfacceranno tramite delle interfacce specifiche per quello che ci possono fare:
- Il world ha accesso ai metodi che gli permettono di spostare la telecamera e di ottenere le coordinate del mondo a partire da quelle sullo schermo (nel caso di quando si riceve input dal puntatore).
- L'EngineView ha accesso ai metodi di conversione delle coordinate da world a schermo, metodi per informare la telecamera delle dimensioni della finestra e per ottenere informazioni sulle proporzioni della schermata di gioco (per poter generare bande nere a bordo schermo se necessario).
- al momento della stampa l'EngineView passa il riferimento della ViewCamera ai GraphicsComponentRenderer che si occupano della effettiva stampa a schermo.

Non è stato adottato alcun design pattern particolare se non lo Strategy: le diverse interfacce definiscono infatti una classe Camera generica che può avere l'implementazione che vogliamo, quella adottata nel nostro gioco è una implementazione standard, ma potremmo adottarne una che segue una logica diversa andando a distorcere le cose visualizzate a schermo senza dover modificare in alcun modo le classi che usano la telecamera.

A differenza della view, potrebbero esserci più oggetti che si occupano di muovere la telecamera, per questo motivo è stato definita anche l'interfaccia di chi può farlo (CameraMover) per poter ricevere il riferimento alla telecamera.

#### Input
```mermaid
classDiagram
class InputEventListener {
<<interface>>
+notifyInputEvent(event: InputEvent): void
}
class InputEventProvider {
<<interface>>
+setInputEventListener(listener: InputEventListener): void
+setInputEventFactory(factory: InputEventFactory): void
}
ViewImpl *-- EngineImpl
EngineImpl --|> InputEventListener
ViewImpl --|> InputEventProvider
```
**Problema:** Notificare il controller delle azioni dell'utente sulle periferiche.

**Soluzione:** Usando il pattern Observer abbiamo reso l'implementazione del controller un osservatore di "InputEvent". La view è il provider, il creatore di questi InputEvent e si occupa di notificare il controller quando uno di questi avviene.

```mermaid
classDiagram
ViewImpl *-- InputEventFactoryImpl
ViewImpl --|> InputEventProvider
class InputEventProvider {
<<interface>>
}
InputEventProvider --> InputEventFactory
InputEventFactoryImpl --|> InputEventFactory
class InputEventFactory {
<<interface>>
+filterKeyValue(key: int): Optional<Value>
+pressedValue(value: Value): InputEvent
+releasedValue(value: Value): InputEvent
+mouseDownAtPosition(position: Position2D): InputEvent
+mouseUpAtPosition(position: Position2D): InputEvent
}
InputEventFactory --> InputEvent
class InputEvent {
<<interface>>
+getType(): Type
}
class Type {
<<enum>>
ACTIVE
INACTIVE
}
InputEvent *-- Type
class InputEventPointer {
<<interface>>
+getPosition(): Position2D
}
InputEventPointer --|> InputEvent
class InputEventValue {
<<interface>>
+getValue(): Value
}
class Value {
<<enum>>
UP
DOWN
LEFT
RIGHT
RESET
PAUSE
}
InputEventValue *-- Value
InputEventValue --|> InputEvent
```
**Problema:** Creare una classe di oggetti che rappresenti gli eventi di input indipendentemente dalla tecnologia di View adottata.

**Soluzione:** L'interfaccia InputEvent rappresenta un evento di Input che può essere ricevuto da un InputEventListener. Questa interfaccia viene poi estesa in tipologie secondarie che vanno a specificare un tipo particolare di input come quelle di un puntatore o di un determinato valore.
- Per permettere al provider di generare questi oggetti facilmente abbiamo creato una Factory di eventi che gli viene fornita.
- E' grazie al metodo `filterKeyValue` della factory che i tasti della tastiera vengono convertiti in valori che rappresentano un'azione di gioco.
- Sarà compito del listener filtrare i vari input che riceve per vedere cosa gli interessa e agire di conseguenza.

#### Weapon
```mermaid
classDiagram
class Weapon {
<<interface>>
+setParentEntity(parent: Character): void
+attack(): void
}
class WeaponFactory {
<<interface>>
+pair(weapon: Weapon, character: Character): void
+simpleGun(reloadTime: long, shootSpeed: long, projectileSize: double, damage: int, projectileFactory: EntityFactory): Weapon
+simpleGunPairing(reloadTime: long, shootSpeed: long, projectileSize: double, damage: int, projectileFactory: EntityFactory, character: Character): Weapon
}
WeaponFactory --> Weapon
```

**Problema:** Creare un'arma che possa essere facilmente associata ad un unico personaggio.

**Soluzione:** Una Factory di Weapon fornisce metodi per creare facilmente il tipo di arma desiderato e per associarla ad un personaggio. La armi implementano l'interfaccia Weapon che identifica un tipo di arma generico (pattern Strategy), questo permette di creare armi con comportamenti diversi senza dover modificare il codice che le utilizza (questo non è stato fatto nel progetto solo per questione di tempo): basta creare nuove implementazioni di Weapon e aggiungere i metodi nella factory.

```mermaid
classDiagram
class EntityFactory {
<<interface>>
+createProjectile(team: Character.CharacterType, position: Position2D, direction: Vect, projectileSize: double, damage: int): Projectile
}
EntityFactory --> Projectile
SimpleGun *-- EntityFactory
SimpleGun --|> Weapon
class Projectile {
<<interface>>
+setDamage(damage: int): void
}
class ActiveEntity {
<<interface>>
}
Projectile --|> ActiveEntity
class Weapon {
<<interface>>
}
```

**Problema:** Creare un'arma che possa sparare dei proiettili.

**Soluzione:** SimpleGun implementa l'interfaccia Weapon e fa riferimento ad una EntityFactory per generare nuovi proiettili; questi infatti sono delle ActiveEntiy, entità che si possono muovere e collidere con altre. SimpleGun alla creazione accetta parametri per impostare la velocità di sparo, la dimensione dei proiettili, la loro velocità e danno. Dunque solo da SimpleGun si potrebbero creare diversi tipi di armi.

### Marchi Luca
### Monaco Andrea
# Capitolo 3 - Sviluppo
Expand All @@ -298,6 +613,24 @@ Usati molto frequentemente, due esempi:
Un esempio: https://github.com/frabitta/OOP23-gfight/blob/1f46024e7a794b0b54e1317836fbd691eb322ea7/src/main/java/gfight/view/impl/MenuPanel.java#L95

### Bittasi Francesco
#### Utilizzo di Stream
Adottate frequentemente per la gestione di elenchi di dati:
- https://github.com/frabitta/OOP23-gfight/blob/fb4012549a225ac1774cfe5618e629f0fbe8a238/src/main/java/gfight/engine/input/impl/InputEventFactoryImpl.java#L18
- paint in canvas
- PolygonGraphicsRenderer

#### Utilizzo di Optional
Usati per convertire i tasti premuti in possibili Value:
- https://github.com/frabitta/OOP23-gfight/blob/fb4012549a225ac1774cfe5618e629f0fbe8a238/src/main/java/gfight/engine/input/impl/InputEventFactoryImpl.java#L18

#### Sincronizzazioni dei thread
nella classe EngineImpl
- Semaphore per input: processInput, notifyInputEvent
- sincronizzazione tramite wait e notify per gli stati dell'engine: holdPageUntilNotified, changeStatus

#### Game as a lab
Ho preso ispirazione dal codice di Game as a lab per la base dell'engine e della view, in particolare per la scrittura della classe `Canvas`.

### Marchi Luca
### Monaco Andrea
# Capitolo 4 - Commenti finali
Expand All @@ -309,4 +642,10 @@ tra di noi e ad affrontare i problemi insieme per trovare le migliori soluzioni.
la sfera personale, penso di aver fatto il mio meglio ed essermi impegnato per quanto riuscissi e sono soddisfatto di
ciò che ho realizzato. Per quanto ci siano delle parti di codice un po' macchinose, come la lettura della mappa da file, o il salvataggio delle statistiche, credo di essere riuscito a sviluppare codice riutilizzabile e facilmente
manutenibile.
### Bittasi Francesco
Sono abbastanza soddisfatto di quanto abbiamo prodotto: le scelte di design che abbiamo fatto ci hanno permesso di apportare numerose modifiche in corso d'opera, ma queste aggiunte/cambiamenti sono stati facili da implementare e non hanno mai rotto il resto del codice: segno che il design fatto a priori era efficace per i nostri obiettivi.
L'unione delle diverse parti di progetto svolte individualmente è stata immediata e senza problemi, potremmo anche implementare nuove feature nel gioco senza modificare il resto del codice: armi, tipi di giocatori e nemici diversi, grafica, etc.
Ci sono diversi casi in cui avrei potuto applicare design Pattern che non ho applicato o seguire tecniche di programmazione java più avanzate, ad esempio avrei potuto pensare di seguire il Decorator per creare le sottocategorie di input ed elementi grafici.
Avevo come parte del progetto una componente fondamentale per permetterne un funzionamento basilare: la gestione della grafica e degli input. Sono due elementi che portano molta soddisfazione da ragionare e implementare in quanto danno un immediato riscontro visivo; guardando indietro avrei potuto cedere l'implementazione delle armi ad un altro membro del gruppo per bilanciare meglio il carico di lavoro visto che anche solo quelle due hanno impiegato gran parte del tempo che ho dedicato al progetto.
Non credo svilupperemo ancora questo gioco, ma è possibile che nel tempo libero io prenda l'engine e la view di questo per sviluppare un gioco diverso.
## 4.2 Difficoltà incontrate e commenti per i docenti

0 comments on commit dc63c88

Please sign in to comment.