XNA Gaming Night Bonn

Preview:

Citation preview

GrundlagenChristopher Schleideni-chschl@microsoft.com

www.sechsta-sinn.de

Agenda

• Überblick• Grundlage/Fenster erzeugen• 2D Grafik laden/anzeigen• Eingabe(n) abfragen• 2D Sprite bewegen• Kollionsabfrage• Animation• Sound

XNA ÜberBlick

History

02006 Dez – XNA Game Studio Express 1.002007 April – XNA Game Studio Express 1.0 Refresh02007 Dez – XNA Game Studio 2.002008 Okt – XNA Game Studio 3.002009 März – XNA Game Studio 3.1

Was ist XNA?

• XNA (XNA’s Not Acronymed)• Framework zur Spieleentwicklung (2D & 3D) für

Windows, Xbox 360 und Zune (auch Zune HD)• Übernimmt Funktionen wie grafische Ausgabe,

Wiedergabe von Audio-Dateien, Abfragen von Eingabegeräten

Was ist XNA nicht?

0 “Spiele-Engine”/Midleware wie z.B. Unity, Unreal Engine oder CryEngine0 Keine Spielmechnismen, Level Editoren,

Kollisionsbehandlung etc.

0ABER:Viele Beispiele, komplette Spiele (Starter Kits) direkt von Microsoft zur freien Verwendung

Entwicklung mit XNA

0Entwickelt wird in C#0 Keine DirectX Kenntnisse notwendig0 Einzige andere wichtige Sprache ist HLSL (High Level

Shader Language) zur Shaderprogrammierung

0XNA Anwendungen 0 Managed Code, alles wird von CLR ausgeführt0 rufen .NET und DirectX Funktionen auf

Application Model

0Fenstermanagement

0 Initialisieren des Graphics-Device

0Bereitstellen des Game-Loop

//// init_d3d.cpp - Initializing Direct3D9//// Copyright 2004 by Ken Paulson//// This program is free software; you can redistribute it and/or modify it// under the terms of the Drunken Hyena License. If a copy of the license was// not included with this software, you may get a copy from:// http://www.drunkenhyena.com/docs/DHLicense.txt//#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers#include <D3DX9.h>#include "../common/dhWindow.h"#include "../common/dhD3D.h"#include "../common/dhUtility.h"#include "../Common/dhUserPrefsDialog.h"

// This is causes the required libraries to be linked in, the same thing can be accomplished by// adding it to your compiler's link list (Project->Settings->Link in VC++),// but I prefer this method.#pragma comment(lib,"d3d9.lib")#pragma comment(lib,"dxerr9.lib")

// Forward declarations for all of our functions, see their definitions for more detailLRESULT CALLBACK default_window_proc(HWND p_hwnd,UINT p_msg,WPARAM p_wparam,LPARAM p_lparam);HRESULT init_scene(void);void kill_scene(void);HRESULT render(void);

void InitVolatileResources(void);void FreeVolatileResources(void);

// The name of our application. Used for window and MessageBox titles and error reportingconst char *g_app_name="Initializing Direct3D9";

// Our screen/window sizes and bit depth. A better app would allow the user to choose the// sizes. I'll do that in a later tutorial, for now this is good enough.const int g_width=640;const int g_height=480;const int g_depth=16; //16-bit colour

// Our global flag to track whether we should quit or not. When it becomes true, we clean// up and exit.bool g_app_done=false;

// Our main Direct3D interface, it doesn't do much on its own, but all the more commonly// used interfaces are created by it. It's the first D3D object you create, and the last// one you release.IDirect3D9 *g_D3D=NULL;

// The D3DDevice is your main rendering interface. It represents the display and all of its// capabilities. When you create, modify, or render any type of resource, you will likely// do it through this interface.IDirect3DDevice9 *g_d3d_device=NULL;

//Our presentation parameters. They get set in our call to dhInitDevice, and we need them//in case we need to reset our application.D3DPRESENT_PARAMETERS g_pp;

//******************************************************************************************// Function:WinMain// Whazzit:The entry point of our application//******************************************************************************************int APIENTRY WinMain(HINSTANCE ,HINSTANCE ,LPSTR ,int ){bool fullscreen;HWND window=NULL;D3DFORMAT format;HRESULT hr;dhUserPrefs user_prefs(g_app_name);

// Prompt the user, Full Screen? Windowed? Cancel? // Prompt the user for their preferences if (!user_prefs.QueryUser()) { dhLog("Exiting\n"); return 0; }

fullscreen = user_prefs.GetFullscreen();

// Build our window. hr=dhInitWindow(fullscreen,g_app_name,g_width,g_height,default_window_proc,&window); if(FAILED(hr)){ dhLog("Failed to create Window",hr); return 0; }

//Build the D3D object hr=dhInitD3D(&g_D3D); if(FAILED(hr)){ dhKillWindow(&window); dhLog("Failed to create D3D",hr); return 0; }

//Find a good display/pixel format hr=dhGetFormat(g_D3D,fullscreen,g_depth,&format); if(FAILED(hr)){ dhKillWindow(&window); dhLog("Failed to get a display format",hr); return 0;

}

DWORD adapter = user_prefs.GetAdapter(); D3DDEVTYPE dev_type = user_prefs.GetDeviceType();

//Initialize our PresentParameters dhInitPresentParameters(fullscreen,window,g_width,g_height,format,D3DFMT_UNKNOWN,&g_pp);

//Create our device hr=dhInitDevice(g_D3D,adapter,dev_type,window,&g_pp,&g_d3d_device); if(FAILED(hr)){ dhKillD3D(&g_D3D,&g_d3d_device); dhKillWindow(&window); dhLog("Failed to create the device",hr); return 0; }

//One-time preparation of objects and other stuff required for rendering init_scene();

//Loop until the user aborts (closes the window,presses the left mouse button or hits a key) while(!g_app_done){ dhMessagePump(); //Check for window messages

hr=g_d3d_device->TestCooperativeLevel();

if(SUCCEEDED(hr)){ hr=render(); //Draw our incredibly cool graphics }

//Our device is lost if(hr == D3DERR_DEVICELOST || hr == D3DERR_DEVICENOTRESET){

dhHandleLostDevice(g_d3d_device,&g_pp,hr);

}else if(FAILED(hr)){ //Any other error

g_app_done=true; dhLog("Error rendering",hr); }

}

//Free all of our objects and other resources kill_scene();

//Clean up all of our Direct3D objects dhKillD3D(&g_D3D,&g_d3d_device);

//Close down our window dhKillWindow(&window);

//Exit happily return 0;}

//******************************************************************************************// Function:InitVolatileResources// Whazzit:Prepare any objects that will not survive a device Reset. These are initialized// separately so they can easily be recreated when we Reset our device.//******************************************************************************************void InitVolatileResources(void){

//In this lesson there is nothing that needs to be done here.

}//******************************************************************************************// Function:FreeVolatileResources// Whazzit:Free any of our resources that need to be freed so that we can Reset our device,// also used to free these resources at the end of the program run.//******************************************************************************************void FreeVolatileResources(void){

//This sample has no resources that need to be freed here.

}//******************************************************************************************// Function:init_scene// Whazzit:Prepare any objects required for rendering.//******************************************************************************************HRESULT init_scene(void){HRESULT hr=D3D_OK;

InitVolatileResources();

return hr;

}

//******************************************************************************************// Function:kill_scene// Whazzit:Clean up any objects we required for rendering.//******************************************************************************************void kill_scene(void){

FreeVolatileResources();

}

//******************************************************************************************// Function: render// Whazzit:Clears the screen and then presents the results.// If we were doing any real drawing, it would go in this function between// the BeginScene() & EndScene().//******************************************************************************************HRESULT render(void){HRESULT hr;

//Clear the buffer to our new colour. hr=g_d3d_device->Clear(0, //Number of rectangles to clear, we're clearing everything so set it to 0 NULL, //Pointer to the rectangles to clear, NULL to clear whole display D3DCLEAR_TARGET, //What to clear. We don't have a Z Buffer or Stencil Buffer 0x00000000, //Colour to clear to (AARRGGBB) 1.0f, //Value to clear ZBuffer to, doesn't matter since we don't have one 0 ); //Stencil clear value, again, we don't have one, this value doesn't matter if(FAILED(hr)){ return hr; }

//Notify the device that we're ready to render hr=g_d3d_device->BeginScene(); if(FAILED(hr)){ return hr;

}

// //All rendering goes here //

//Notify the device that we're finished rendering for this frame g_d3d_device->EndScene();

//Show the results hr=g_d3d_device->Present(NULL, //Source rectangle to display, NULL for all of it NULL, //Destination rectangle, NULL to fill whole display NULL, //Target window, if NULL uses device window set in CreateDevice NULL );//Unused parameter, set it to NULL

return hr;}

//******************************************************************************************// Function:default_window_proc// Whazzit:This handles any incoming Windows messages and sends any that aren't handled to// DefWindowProc for Windows to handle.//******************************************************************************************LRESULT CALLBACK default_window_proc(HWND p_hwnd,UINT p_msg,WPARAM p_wparam,LPARAM p_lparam){ switch(p_msg){ case WM_KEYDOWN: // A key has been pressed, end the app case WM_CLOSE: //User hit the Close Window button, end the app case WM_LBUTTONDOWN: //user hit the left mouse button g_app_done=true; return 0; } return (DefWindowProc(p_hwnd,p_msg,p_wparam,p_lparam));

using System;using System.Collections.Generic;using System.Linq;using Microsoft.Xna.Framework;using Microsoft.Xna.Framework.Audio;using Microsoft.Xna.Framework.Content;using Microsoft.Xna.Framework.GamerServices;using Microsoft.Xna.Framework.Graphics;using Microsoft.Xna.Framework.Input;using Microsoft.Xna.Framework.Media;using Microsoft.Xna.Framework.Net;using Microsoft.Xna.Framework.Storage;

namespace WindowsGame1{ public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch;

public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } protected override void Initialize() { base.Initialize(); }

protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice);

}

protected override void UnloadContent() { }

protected override void Update(GameTime gameTime) { base.Update(gameTime); }

protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue);

base.Draw(gameTime); } }}

Application Model

0 Initialize()

0LoadContent()

0UnloadContent()

0Update()

0Draw()

Gameloop Bestandteile

0UpdateAktualisiert die Spiellogik der Anwendung

0DrawZeichnet veränderten Zustand der Anwendung auf den Bildschirm

Timing

0Default: Fixed-Timestep mit Ziel-Framerate 60 FPS

0Pro Sekunde wird 60 mal Update aufgerufen0 d.h. 16,67 Millisekunden pro Frame

0So oft wie möglich dazu noch Draw

Timing verwenden

0Update und Draw besitzen ein GameTime Objekt0 z.B. Zeitspanne zwischen zwei Aufrufen von Update()

oder Draw()

protected override void Update(GameTime gameTime)

{int elapsed = gameTime.ElapsedGameTime.Milliseconds;

}

Content Pipeline

0 Inhalte werden nicht im Original-Format geladen

0 Inhalte werden mittels Content Pipeline in eigenes XNA Format übersetzt (XNA Binary, .xnb)

0Dabei werden diese für die Verwendung vorbereitet:0 3D Modelle in XNA eigene Datenstruktur laden0 z.B. 3D-Modelle mit Textur zusammenfügen

Content Pipeline: Flow

Vordefinierte Prozessoren

0Unterstütze Formate:0 Grafiken (.bmp, .jpg, .png, .dds, .tga)0 3D Modelle (.x, .fbx, .obj)0 Schriften (.spritefont)0 Shader-Dateien (HLSL) (.fx)0 Audio-Dateien (.wav, .wma, .mp3)0 XACT-Soundprojekte (.xap)

Content Pipeline: Importer/Processor

Grafiken anzeigen

1. Inhalt laden

0Grafiken (Sprites) werden durch Klasse Texture2D repräsentiert

Texture2D mySprite = Content.Load<Texture2D>(“Held”);

Keine Dateiendung angeben!Content in LoadContent laden

2. Anzeigen - SpriteBatch

02D Grafiken werden mittels SpriteBatch gezeichnet

0SpriteBatch kann 1 bis n Sprites mit gleichen Einstellungen zeichnen

0Umschlossen von SpriteBatch.Begin() und SpriteBatch.End()

2. Anzeigen – SpriteBatch.Draw

spriteBatch.Begin();

spriteBatch.Draw( Texture2D, Vector2, Color );

…spriteBatch.Draw( Texture2D, Vector2,

Color );

spriteBatch.End();

Aufgabe 1

0Sprite laden, anzeigen, in der Mitte des Bildschirms rotieren (um Mittelpunkt der Grafik) und Rot färben

1. XNA Gamestudio Projekt erstellen2. Grafik in Projekt einfügen (Unterpunkt Content)3. Laden mit Content.Load4. Mittels SpriteBatch und Draw Funktion anzeigen5. Rotation: SpriteBatch.Draw ist mehrfach überladen6. Mitte des Bildschirms: GraphicsDevice.Viewport.....

Lösung – Aufgabe 1

private Texture2D MySprite;private float RotationAngle = 0.0f;

// Load our sprite through the content pipelineMySprite = Content.Load<Texture2D>("Controller");

// Update rotation according to elapsed timeRotationAngle += (float)gameTime.ElapsedGameTime.TotalSeconds; RotationAngle %= MathHelper.Pi * 2.0f;

Lösung – Aufgabe 1// Begin sprite drawingspriteBatch.Begin();

// Position where the sprite should be displayed on the screenVector2 pos = new Vector2(GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height / 2);

// Center point of rotationVector2 origin = new Vector2(MySprite.Width / 2, MySprite.Height / 2);

// Draw the spritespriteBatch.Draw(MySprite, pos, null, Color.Red, RotationAngle, origin, 1.0f, SpriteEffects.None,0f);

// End sprite drawingspriteBatch.End();

SpriteBatch.Draw Funktion

0Ausschnitte0Rotation0Ursprung versetzen0Skalierung

0Effekte (umdrehen der Grafik)

0Einfärben der Grafik0Layer

Stapelverarbeitung von Sprites

0SpriteBatch.Begin()0 Sprites anzeigen

0SpriteBatch.End()

0Begin (SpriteBlendMode blendMode,SpriteSortMode sortMode,

SaveStateMode stateMode,Matrix transformMatrix

)

• Additive• AlphaBlend• None

• Deferred• BackToFront• FrontToBack• Texture

• Immediate

• SaveState• None

BackBuffer

0Gezeichnet wird auf ein sog. RenderTarget

0RenderTarget ist ein Buffer in dem die Pixel eines Bildschirm gespeichert werden

0Default RenderTarget wird auch BackBuffer genannt

BackBufferBackBuffer FrontBuffer

Eigenschaften des Displays

Display

0Auflösung ändern:0 graphicsDeviceManager.

PreferredBackBufferWidth = 1280;0 graphicsDeviceManager.

PreferredBackBufferHeight = 720;0Vollbild

0 graphicsDeviceManager.IsFullScreen = true;

0Viewport0 Teil eines RenderTargets, muss nicht gesamtes

RenderTarget ausfüllen

Text anzeigen

Schrift anzeigen

0XNA verwendet normale Windows TrueType Fonts

0ContentPipeline erzeugt daraus Textur (SpriteFont)0 Steuerung mittels XML-Datei

0Font-Name0Font-Größe0Normal, Fett, Kursiv0Abstände0Zeichenbereiche

SpriteFont

0LadenmyFont = Content.Load<SpriteFont>(“XML-Datei”);

0AnzeigenSpriteBatch.DrawString(SpriteFont, String, Vector2, Color );

0DrawString() ist ähnlich überladen wie Draw

Nützliche Funktionen

0 Höhe und Breite eines Strings abfragenSpriteFont.MeasureString( String )

0 XNA Fonts0 Kooten.ttf0 Linds.ttf0 Miramo.ttf0 Bold Miramob.ttf0 Peric.ttf0 Pericl.ttf0 Pesca.ttf0 Pescab.ttf

Eingabe

Eingabe

0XNA ermöglicht:0 Mauseingaben Klasse: Mouse0 Tastatureingaben Klasse: Keyboard0 Xbox-Controller Klasse: Gamepad

0 Jeweils: GetState() Methode

Eingabe: Tastatur

0Tastenstatus abfragenKeyboard.GetState().IsKeyDown(Keys.Up)

0Tastendruck abfragenKeyboardState currentKBState = Keyboard.GetState();

if( currentKBState.IsKeyDown(key) && !previousKBState.IsKeyDown(key) ) { … }

previousKBState = currentKBState;

Eingabe: Xbox-Controller

0 ThumbstickGamePadState.ThumbSticks.Left / .Right

0 DPad / ButtonGamePadState.Buttons.DPadLeft …GamePadState.Buttons.A …

0 VibrationGamePad.SetVibration(…)

Aufgabe 2

0Sprite mit Tastatur/Maus bewegen

0Position des Sprites auf Bildschirm anzeigen

Lösung – Aufgabe 2 // Get new keyboard stateKeyboardState keyboardState = Keyboard.GetState();

// Calculate the amount of moving with the elapsed time since the last frame, this gives us framerate independent movementfloat movement = (float)gameTime.ElapsedGameTime.TotalMilliseconds * 0.5f;

// Left/Rightif (keyboardState.IsKeyDown(Keys.Left)) {

spritePosition.X -= movement;}if (keyboardState.IsKeyDown(Keys.Right)) {

spritePosition.X += movement;}// Up/Downif (keyboardState.IsKeyDown(Keys.Up)) {

spritePosition.Y -= movement;}if (keyboardState.IsKeyDown(Keys.Down)) {

spritePosition.Y += movement;}

Lösung – Aufgabe 2// Draw the sprite (White as color to disable any color effects)spriteBatch.Draw(MySprite, spritePosition, Color.White); // Text to displaystring displayText = "Position: " + spritePosition.X + " : " +

spritePosition.Y;

// Display text in upper left corner of viewportVector2 displayPosition = new Vector2(10, 10);

// Display text using our loaded Font and in red colorspriteBatch.DrawString(MyFont, displayText, displayPosition, Color.Red);

Kollisionsbehandlung

Mathematische Funktionen: Vektoren

0 Klassen Vector2, Vector3 und Vector40 Grundrechnenarten

0Add(), Multiply(), etc.

0 Vector2.Distance()0Abstand zwischen zwei Vektoren

0 Vector2.Length()0 Länge eines Vektors

0 Vector2.Reflect()0Veränderter Vektor nach einer Kollision

+ =

Matrizen

0Klasse Matrix für Transformation

0Wird insbesondere bei 3D verwendet

0Hilfsfunktionen:0 Matrix.CreateRotation(...)0 Matrix.CreateTranslation(...)0 …

Rectangles

Kollisionen erkennen

Aufgabe 3

1. Kollisionen mit einem anderen Sprite im 2D-Raum feststellen und diese behandeln

2. Kollisionserkennung implementieren, die verhindert, dass ein Sprite den sichtbaren Teil des Bildschirmes verläßt

Lösung – Aufgabe 3

// Load Sound EffectsoundEffect = cm.Load<SoundEffect>("thunder");

// Play SoundsoundEffect.Play();

Nachtrag Aufgabe 3

0Pixel-Genaue Kollision zwischen Sprites

0Collision Series auf creators.xna.com

Sound & Musik

Sound

0Wiedergabe von Sound-Dateien (wav, mp3, wma)

0SoundEffect- und Song-Klasse

0Abspielen mittels Play() Methode

0Werden mittels der Content Pipeline geladen

Sound

0SoundEffect-Klasse bietet zusätzliche 3D-Sound Funktion (Play3D)

0 Sounds im 3D-Raum positionieren0 Sounds pitchen0 Sounds loopen

Musik

0 MediaPlayer-Klasse Funktionen zum:0 Abspielen, Stoppen, Resumen von Songs

0 Abspielen einer Datei0 MediaPlayer.Play(SongObject);

0 Musik-Dateien werden in einer Endlosschleife abgespielt

0 Kann durch eigene Musik von der Xbox360 „ersetzt“ werden

Aufgabe 4

0Wiedergabe von Sounds0 Bei Bewegung eines Objektes0 Kollision eines Objektes

0Wiedergabe von Musik0 Permanente Hintergrund-Musik

Lösung – Aufgabe 4

soundEffect = Content.Load<SoundEffect>("thunder");

KeyboardState keyboardState = Keyboard.GetState();if( keyboardState.IsKeyDown( Keys.Space ) && lastKeyState.IsKeyUp( Keys.Space ) ){

soundEffect.Play();}

soundSong = Content.Load<Song>("maid");

MediaPlayer.Play( soundSong );

Alternative XACT

0Cross-platform Audio Creation Tool

0Tool zum Erzeugen von Sound-Cues

0Erlaubt das Erstellen einer Vielzahl von Effekten und Sound-Kombinationen

0Erstellte Cues werden im Quelltext nur noch abgespielt, keine weitere Programmierung nötig

XACT

Animationen

Animationen

0Charakteranimation typischerweise: viele einzelne Frames

0Möglichkeit 1: Texure2D pro Frame

0Sinnvoller:0 Große Textur mit allen Frames0 Auswahl über Rectangle

Animationsparameter

0Animations-Geschwindigkeit0Anzahl der Frames0Größe eines Frames

Rectangle rc = new Rectangle();rc.Width = 175;rc.Height = 220;

rc.X = (currentFrame % 4) * 175;cc.Y = (currentFrame / 4) * 220;

175px

220p

x

Aufgabe 5 – Animation

1. Sprite „BossWalkRight.png“ laden0 Breite: 175px 0 Höhe: 220px 0 1 Animationsrichtung pro Zeile0 6 Frames pro Animation

2. Lauf-Animation anzeigen

Lösung – Aufgabe 5const int SpriteFrameWidth = 175; const int SpriteFrameHeight = 220;const int AnimationSpeed = 75; const int AnimationNumFrames = 6;

int animationTimer;int animationFrame;

// Updateint elapsed = gameTime.ElapsedGameTime.Milliseconds;animationTimer += elapsed;while( animationTimer > AnimationSpeed ) {

animationTimer -= AnimationSpeed;animationFrame = (animationFrame + 1) %

AnimationNumFrames;}

Animationsparameter

Lösung – Aufgabe 5

// DrawRectangle rect = new Rectangle();

rect.Width = SpriteFrameWidth;rect.Height = SpriteFrameHeight;

rect.X = animationFrame * SpriteFrameWidth;rect.Y = 1 * SpriteFrameHeight;

spriteBatch.Draw( texture, new Vector2( 0, 0 ), rect, Color.White );

// animationsdatei für bunkermenschen soldat gewehr// christopher schleiden 2009// ---------------------------------------------------------------

// anzahl frames gesamtNUMFRAMES 70

// able_to == ALL, MOVE, ATTACK, SHOOT, DIE, NULL

// ----------------------------------------------------------------------------------------------------//n name sta num req

go2 able_to delay statecallname

// ----------------------------------------------------------------------------------------------------// AUFRECHT BEWEGEN// ----------------------------------------------------------------------------------------------------0 steht 0 8 -1

0 NULL 100 STANDDEFAULT

1 begin_1 8 8 -12 MOVE 100 STANDMOVE_NORMAL_START

2 begin_2 16 8 13 MOVE 100 STAND

3 laufen_1 24 8 2 4MOVE 100 STAND

MOVE_NORMAL_RUN4 laufen_2 32 8 3 5

MOVE 100 STAND5 laufen_3 40 8 4 6

MOVE 100 STAND6 laufen_4 48 8 5 7

MOVE 100 STAND7 laufen_5 56 8 6 8

MOVE 100 STAND8 laufen_6 64 8 7 3

MOVE 100 STAND9 ende_1 72 8 -1

10 MOVE 100 STANDMOVE_NORMAL_END

10 ende_2 80 8 90 MOVE 100 STAND

// ----------------------------------------------------------------------------------------------------// AUFRECHT STERBEN (HELDENTOD ;)// ----------------------------------------------------------------------------------------------------11 sterben_steht_1 456 8 0

12 NULL 250 DIEDIE_NORMAL

12 sterben_steht_2 464 8 1113 NULL 250 DIE

13 sterben_steht_3 472 8 1214 NULL 250 DIE

// das ist die zeit die die leiche liegen bleibt14 tot_steht 480 8 13 66

NULL 60000 DIE// ----------------------------------------------------------------------------------------------------// AUFRECHT TRINKEN// ----------------------------------------------------------------------------------------------------15 trinken_1 184 8 0 16

NULL 100 STAND IDLE_116 trinken_2 192 8 15 17

NULL 200 STAND17 trinken_3 200 8 16 18

NULL 500 STAND18 trinken_2 192 8 17 19

NULL 200 STAND19 trinken_1 184 8 18 0

NULL 100 STAND// ----------------------------------------------------------------------------------------------------// AUFRECHT HELM AB// ----------------------------------------------------------------------------------------------------

Quellen

0Folien basieren teilweise auf Originalen von Ingo Köster

Danke für Eure Aufmerksamkeit!

Und jetzt…?

Wettbewerb!

Um XX:XX kürt die fachkundige Jury das beste/interessanteste/spielbarste Spiel!

Recommended