|
|
# Cvičenie 5
|
|
|
|
|
|
__BREAKING CHANGES: PREMENOVANÉ ROZHRANIA - PRIDANÁ PREDPONA `I` (`ICommand, IWorld, IPhysics`)__ aby boli dodržané C# naming conventions
|
|
|
|
|
|
## 5.1 There are no ghosts here
|
|
|
|
|
|
Pri implementácií `Move` ste zatiaľ neriešili kolíziu s prostredím. Napriek tomu, že už od nepamäti poznáme spôsob, ako prechádzať cez steny (nazvali ho dvere), v hre by bolo implementovať funkcionalitu, ako postavičke zabrániť v pohybe cez stenu.
|
|
|
|
|
|
Každý actor v sebe udržuje referenciu na svet, v ktorom existuje. Viete sa k nej dostať cez `GetWorld()`. Svet má v sebe uložených všetkých actorov a takiež pozná rozloženie mapy - máte dostupnú metódu `bool IntersectsWithWall(IActor)`. Použite ju a doplňte implementáciu tak, aby sa pomocou Move nedalo chodiť cez stenu.
|
|
|
|
|
|
## 5.2 Newton's legacy
|
|
|
|
|
|
Píše sa rok 1687 a sir Isaac Newton práve publikoval dielo, ktoré zmenilo svet, s názvom _Principia_. Zažite tento slávny moment a objavte gravitáciu:
|
|
|
|
|
|
Vytvorte si triedu `Gravity` v priečinku `Commands`. Nech implementuje rozhranie `IPhysics` - jedná sa o okrášlený `ICommand` - je tu navyše metóda `SetWorld` - táto sa volá automaticky v rámci frameworku pri nastavení fyziky sveta a dostanete v nej referenciu na `IWorld` - uložte si ju, budete ju potrebovať.
|
|
|
|
|
|
`IPhysics.Execute()` sa automaticky volá stále v hernej slučke - viete teda v `Gravity` implementovať postupné padanie predmetov:
|
|
|
|
|
|
- Každý actor implementuje 2 metódy - `bool IsPhysicsEnabled()` a `void SetPhysics(bool isPhysicsEnabled)` - pomocou nich dokážeme regulovať, či bude daný actor ovplyvnený fyzikou alebo nie - môžeme takto nechať nejaký predmet lietať vo vzduchu.
|
|
|
- `IWorld` má v sebe metódu `GetActors()` - táto vráti zoznam všetkých actorov vo svete.
|
|
|
- Zatiaľ využite `Move` na pád, ale musíte stále vytvárať nové inštancie (dočasne zakomentujte typovú kontrolu pre `IMovable` v `Move` (a upravte ho, nech akceptuje `IActor` nie `IMovable`) - za chvíľu sa k tomu vrátime a ukážeme si niečo viac o práci s typovými parametrami a Action design pattern) - nevýhoda je zvytočné vytváranie množstva objektov.
|
|
|
- v cykle, napríklad `foreach` si implementujte pád, nezabudnite zohľadniť či je daný predmet ovplyvnený gravitáciou.
|
|
|
|
|
|
## 5.3 Aaaand `Action<T>`
|
|
|
|
|
|
V priečinku commands si vytvorte `Interface IAction`:
|
|
|
|
|
|
```csharp
|
|
|
public interface IAction<T>
|
|
|
{
|
|
|
public void Execute(T t);
|
|
|
}
|
|
|
```
|
|
|
|
|
|
ako už deklarácia napovedá, jedná sa o niečo podobné ako `ICommand` ale je tu jeden rozdiel - v rámci execute vieme poslať nejaký parameter. Toto vieme využiť pri našej implementácií pádu - nepotrebujeme vytvárať nový objekt pre každého actora ale použijeme stále ten istý.
|
|
|
|
|
|
Pridajte si do Commands triedu `Fall`, nech implementuje `IAction` - máme tu dva spôsoby implementácie (použite druhý):
|
|
|
|
|
|
```csharp
|
|
|
public class Fall : IAction<IActor>
|
|
|
{
|
|
|
public void Execute(IActor t)
|
|
|
{
|
|
|
//magic with actor
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public class Fall<T> : IAction<T> where T : IActor
|
|
|
{
|
|
|
public void Execute(T t)
|
|
|
{
|
|
|
//magic with something, that implements IActor
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
Takýmto spôsobom sa vyhneme unboxingu a samozrejme aj zbytočnému vytváraniu množstva objektov - to však neznamená, že Command je zlý, ešte sa nám bude hodiť.
|
|
|
|
|
|
Implementujte pád (nezabudnite odstrániť komentáre).
|
|
|
|
|
|
## 5.4 LINQ and Lambdas
|
|
|
|
|
|
Stále prechádzať zoznamy je často nutné a nie veľmi praktické. Našťastie máme funkcionalitu, ktorá nám toto vie zjednodušiť. V C# vieme aplikovať rôzne filtre na kolekcie pomocou fukcií, ktoré dostanú ako vstupný parameter filter (lambda výraz alebo referenciu na funkciu).
|
|
|
|
|
|
Najprv potrebujeme pridať zodpovedajúci namespace (`using System.Linq`) - následne vieme aplikovať tzv. extension methods na rôzne kolekcie, napríklad:
|
|
|
|
|
|
```csharp
|
|
|
List<int> values;
|
|
|
//add some values into the list
|
|
|
|
|
|
List<int> negativeValues = values.Where(x => x < 0).ToList(); //select values smaller than 0, the result is then converted into a list (it is IEnumerable by default)
|
|
|
int smallestValue = values.OrderBy(x => x).First(); //select the smalest value (order them and select the first one)
|
|
|
int firstPositive = values.First(x => x > 0); //select the first positive value from the list
|
|
|
int numberOfPositiveValues = values.Where(x => x > 0).Count(); //count the number of positive values
|
|
|
|
|
|
List<IActor> actors//init and stuff...
|
|
|
|
|
|
IActor actor = actors.Find(a => a.GetName().Equals("player")); //select single entry
|
|
|
```
|
|
|
|
|
|
Samozrejme, dostupných metód je oveľa viac, ako sme tu ukázali, môžete si ich pozrieť napr. cez dopĺňanie kódu alebo na internete.
|
|
|
|
|
|
Modifikujte `foreach` tak, aby prechádzal iba actorov, na ktorých vplýva gravitácia.
|
|
|
|
|
|
_tip: ak trošku predbehneme, existuje aj `foreach` verzia, ktorá akceptuje lambda výraz, tu by to fungovalo nasledovne:_
|
|
|
|
|
|
```csharp
|
|
|
//void DoStuff(IActor actor);
|
|
|
actors.ForEach(a => DoStuff(a));
|
|
|
```
|
|
|
|
|
|
## 5.5 Jump
|
|
|
|
|
|
Vytvorte triedu Jump, ktorá umožní hráčovi skákať, zvážte použité rozhranie.
|
|
|
|
|
|
## 5.6 My very own enemy
|
|
|
|
|
|
Vytvorte triedu `Enemy` (rozširuje `AbstractActor`).
|
|
|
|
|
|
Použite `enemy.png` ako animáciu.
|
|
|
|
|
|
V `Update` implementujte funkcionalitu, že ak sa hráč k nepriateľovi priblíži na vzdialenosť N pixelov, začne ho prenasledovať. Pokiaľ je obeť v nedohľadne, nech sa nepriateľ náhodne prechádza po mape.
|
|
|
|
|
|
```csharp
|
|
|
Random _random = new Random(); //initialize pseudorandom number generator, do this only once
|
|
|
|
|
|
//some awesome code...
|
|
|
|
|
|
int x = random.Next(min,max); //get random number from the given range
|
|
|
``` |
|
|
\ No newline at end of file |