Kitai Délégué de la tasse bleue
Nombre de messages : 2907
Date d'inscription : 01/08/2006
| Sujet: Module Snake (AGS 3.2.1 et supérieures) Jeu 6 Juin 2013 - 10:52 | |
| Suite à une demande de Shai-la, j'avais codé un petit module pour faire un Snake sous AGS. Mais pataud que je suis, j'ai fait une usine à gaz assez difficile à utiliser. Suite à ce déterrement de sujet par Shai-la (encore elle !) sur le forum anglophone, je me suis dit qu'il serait sympa de proposer à nouveau un tel module, simple à utiliser et malgré tout efficacement personnalisable. Snake.ash- Spoiler:
- Code:
-
#define SNAKE_OBJECT_BITTEN 1// These values will be passed to the room #define SNAKE_SELF_BITTEN 2// function on_call so that you can handle #define SNAKE_WALL_HIT 3// what happens in the relevant situations
struct Snake { /// Randomly places the object in the room so that the Snake can bite it import static bool PlaceObjectOnRandomFreeCoordinates(Object* stuff); /// Makes the Snake move in DIRECTION using the specified views to draw its parts, updating its position every DELAY cycle import static function Init(int headview, int bodyview, int tailview = -1, int delay = 20, eKeyCode direction = eKeyRightArrow); /// Removes N parts from the Snake's body import static function Pop(int n = 1); /// Makes the Snake move import static function Start(); /// Stops the Snake moving import static function Stop(); /// Clear the Snake from the screen and frees the main character's view back import static function Clear(); /// Gets/Sets how many parts will be added to the Snake's body import static attribute int Queue; import static int get_Queue(); // $AUTOCOMPLETEIGNORE$ import static int set_Queue(int n); // $AUTOCOMPLETEIGNORE$ /// Gets/Sets which view is used to draw the Snake's head import static attribute int HeadView; import static int get_HeadView(); // $AUTOCOMPLETEIGNORE$ import static int set_HeadView(int headview); // $AUTOCOMPLETEIGNORE$ /// Gets/Sets which view is used to draw the Snake's body parts import static attribute int BodyView; import static int get_BodyView(); // $AUTOCOMPLETEIGNORE$ import static int set_BodyView(int bodyview); // $AUTOCOMPLETEIGNORE$ /// Gets/Sets which view is used to draw the Snake's tail import static attribute int TailView; import static int get_TailView(); // $AUTOCOMPLETEIGNORE$ import static int set_TailView(int tailview); // $AUTOCOMPLETEIGNORE$ /// Gets/Sets the frequency of updating the Snake's position (every X cycle) import static attribute int Delay; import static int get_Delay(); // $AUTOCOMPLETEIGNORE$ import static int set_Delay(int delay); // $AUTOCOMPLETEIGNORE$ /// Gets/Sets the room object which the Snake will be happy to bite import static attribute Object* BodyObject; import static Object* get_BodyObject(); // $AUTOCOMPLETEIGNORE$ import static void set_BodyObject(Object* bodyobject); // $AUTOCOMPLETEIGNORE$ /// Gets/Sets the room object which the Snake will be remorseful to bite readonly import static attribute Object* BittenObject; import static Object* get_BittenObject(); // $AUTOCOMPLETEIGNORE$ /// Gets how many parts the Snake's body contains readonly import static attribute int Length; import static int get_Length(); // $AUTOCOMPLETEIGNORE$ /// Gets the width of any atomic part of the Snake readonly import static attribute int Width; import static int get_Width(); // $AUTOCOMPLETEIGNORE$ /// Gets the height of any atomic part of the Snake readonly import static attribute int Height; import static int get_Height(); // $AUTOCOMPLETEIGNORE$ }; Snake.asc- Spoiler:
- Code:
-
// TODO // // Vérifier la gestion automatique des diagonales
//#define AREA_LEFT 0 //#define AREA_TOP 0 //#define AREA_RIGHT Room.Width //#define AREA_BOTTOM Room.Height
#define AREA_LEFT Room.LeftEdge #define AREA_TOP Room.TopEdge #define AREA_RIGHT Room.RightEdge #define AREA_BOTTOM Room.BottomEdge
// Ajoute une valeur à un tableau dynamique int[] ArrayInt_Add(int array[], int oldsize, int newvalue) { if (oldsize < 0) oldsize = 0; int ret[] = new int[oldsize+1]; int i = 0; while (i < oldsize) { ret[i] = array[i]; i++; } ret[i] = newvalue; return ret; }
int X[], Y[], Loop[], Keys[], HeadLoop[], BodyLoop[], TailLoop[]; eKeyCode Dir; DynamicSprite* SurfaceSprite; int SnakeRoom, HeadView, BodyView, TailView, Delay, Width, Height, Length, KeySize, FormerX, FormerY, FormerSprite = -1, IdObject = -1, IdBitten = -1;
static int Snake::get_HeadView() { return HeadView; } static int Snake::set_HeadView(int n) { if (n < 1) n = 1; if (n > Game.ViewCount) n = Game.ViewCount; HeadLoop = new int[4]; int i = 0, loops = Game.GetLoopCountForView(n), loopwithframe; if (loops < 1) AbortGame("SnakeError: No loop in head view %d", n); bool noframe = true; while ((i < loops) && (i < 4)) { if (Game.GetFrameCountForLoop(n, i) > 0) { noframe = false; HeadLoop[i] = i; loopwithframe = i; } else HeadLoop[i] = -1; i++; } if (noframe) AbortGame("SnakeError: No frame to display in head view %d", n); i = 0; while (i < 4) { if (HeadLoop[i] < 0) HeadLoop[i] = loopwithframe; i++; } HeadView = n; player.LockView(n); } static int Snake::get_BodyView() { return BodyView; } static int Snake::set_BodyView(int n) { if (n < 1) n = 1; if (n > Game.ViewCount) n = Game.ViewCount; BodyLoop = new int[8]; int i = 0, loops = Game.GetLoopCountForView(n), loopwithframe; if (loops < 1) AbortGame("SnakeError: No loop in body view %d", n); bool noframe = true; while ((i < loops) && (i < 4)) { if (Game.GetFrameCountForLoop(n, i) > 0) { noframe = false; BodyLoop[i] = i; loopwithframe = i; } else BodyLoop[i] = -1; i++; } if (noframe) AbortGame("SnakeError: No frame to display in body view %d", n); i = 0; while (i < 4) { if (BodyLoop[i] < 0) BodyLoop[i] = loopwithframe; i++; } // Si on n'a que les 4 directions, on va utiliser celles-ci pour les diagonales if (loops < 5) { i = 4; while (i < 8) { BodyLoop[i] = BodyLoop[i%4]; i++; } } // Si on a que 5 directions, on va utiliser la 5ème pour calculer toutes les diagonales autres else if ((loops < 6) && (Game.GetFrameCountForLoop(n, 4))) { i = 4; while (i < 8) { BodyLoop[i] = 4; i++; } } // Si on a plus de 5 directions else { if (Game.GetFrameCountForLoop(n, 4)) loopwithframe = 4; i = 4; while (i < 8) { // On assigne la direction elle-même si elle contient des images if (i < Game.GetLoopCountForView(n) && Game.GetFrameCountForLoop(n, i)) BodyLoop[i] = i; // La cinquième direction si elle contient des images else if (loopwithframe > 3) BodyLoop[i] = 4; // Une direction de base si la cinquième ne contient pas d'image else BodyLoop[i] = i%4; i++; } } BodyView = n; } static int Snake::get_TailView() { return TailView; } static int Snake::set_TailView(int n) { if (n < 1) n = 1; if (n > Game.ViewCount) n = Game.ViewCount; TailLoop = new int[4]; int i = 0, loops = Game.GetLoopCountForView(n), loopwithframe; if (loops < 1) AbortGame("SnakeError: No loop in tail view %d", n); bool noframe = true; while ((i < loops) && (i < 4)) { if (Game.GetFrameCountForLoop(n, i) > 0) { noframe = false; TailLoop[i] = i; loopwithframe = i; } else TailLoop[i] = -1; i++; } if (noframe) AbortGame("SnakeError: No frame to display in tail view %d", n); i = 0; while (i < 4) { if (TailLoop[i] < 0) TailLoop[i] = loopwithframe; i++; } TailView = n; } static int Snake::get_Delay() { return Delay; } static int Snake::set_Delay(int delay) { if (delay < 1) delay = 1; Delay = delay; } static int Snake::get_Length() { return Length; } static int Snake::get_Width() { return Width; } static int Snake::get_Height() { return Height; } static Object* Snake::get_BodyObject() { if (IdObject < 0) return null; return object[IdObject]; } static void Snake::set_BodyObject(Object* stuff) { if (stuff == null || SnakeRoom != player.Room || stuff.ID == IdObject) return; if (FormerSprite+1) { object[IdObject].Graphic = FormerSprite; object[IdObject].SetPosition(FormerX, FormerY); } FormerSprite = stuff.Graphic; FormerX = stuff.X; FormerY = stuff.Y; IdObject = stuff.ID; stuff.SetPosition(0, Room.Height); } static Object* Snake::get_BittenObject() { if (IdBitten < 0) return null; return object[IdBitten]; }
bool FreeXY(int x, int y) { x -= GetViewportX(); y -= GetViewportY(); if (GetWalkableAreaAt(x, y) < 1) return false; Character* guy = Character.GetAtScreenXY(x, y); if (guy != null && guy.Solid) return false; Object* stuff = Object.GetAtScreenXY(x, y); if (stuff != null && stuff.ID != IdObject && stuff.Solid) return false; return true; }
static bool Snake::PlaceObjectOnRandomFreeCoordinates(Object* stuff) { if (stuff == null || SnakeRoom != player.Room) return false; DynamicSprite* sprite = DynamicSprite.CreateFromExistingSprite(stuff.Graphic); int x = AREA_LEFT+Random(AREA_RIGHT-AREA_LEFT-1), y = AREA_TOP+Random(AREA_BOTTOM-AREA_TOP-1), i = 0; x -= (x-AREA_LEFT)%Width-sprite.Width/2; y -= (y-AREA_TOP)%Height-sprite.Height; while (!FreeXY(x, y) || (y+sprite.Height>AREA_BOTTOM) || (x+sprite.Width/2>AREA_RIGHT)) { x = AREA_LEFT+Random(AREA_RIGHT-AREA_LEFT-1); x -= (x-AREA_LEFT)%Width-sprite.Width/2; y = AREA_TOP+Random(AREA_BOTTOM-AREA_TOP-1); y -= (y-AREA_TOP)%Height-sprite.Height; i++; if (i > 50000) return false; } stuff.SetPosition(x, y); return true; }
static function Snake::Clear() { SnakeRoom = -1; HeadView = 0; BodyView = 0; TailView = 0; Delay = 0; Width = 0; Height = 0; Length = 0; KeySize = 0; X = null; Y = null; Loop = null; Keys = null; Dir = eKeyRightArrow; if (FormerSprite+1) { object[IdObject].Graphic = FormerSprite; object[IdObject].X = FormerX; object[IdObject].Y = FormerY; FormerSprite = -1; IdObject = -1; } if (SurfaceSprite != null) { SurfaceSprite.Delete(); SurfaceSprite = null; } IdObject = -1; IdBitten = -1; player.UnlockView(); }
static function Snake::Start() { if (player.x+(Width/2) > AREA_RIGHT) player.x = AREA_LEFT+Width/2; if (player.y>AREA_BOTTOM) player.y = AREA_TOP+Height; if (player.x-(Width/2) < AREA_LEFT) player.x = AREA_RIGHT-Width/2; if (player.y-Height < AREA_TOP) player.y = AREA_BOTTOM; player.x -= (player.x-Width/2-AREA_LEFT)%Width; player.y -= (player.y+Height-AREA_TOP)%Height; player.StopMoving(); player.LockView(HeadView); player.FaceLocation(player.x-(10*(Dir==eKeyLeftArrow))+(10*(Dir==eKeyRightArrow)), player.y+(10*(Dir==eKeyDownArrow))-(10*(Dir==eKeyUpArrow))); player.Animate(player.Loop, player.AnimationSpeed, eRepeat, eNoBlock); SnakeRoom = player.Room; } static function Snake::Stop() { SnakeRoom = -1; player.UnlockView(); player.LockView(HeadView); }
static function Snake::Init(int headview, int bodyview, int tailview, int delay, eKeyCode direction) { if (headview < 1) AbortGame("SnakeError: Invalid view specified for HeadView (%d)", headview); if (bodyview < 1) AbortGame("SnakeError: Invalid view specified for BodyView (%d)", bodyview); if (Game.GetLoopCountForView(bodyview) < 1) AbortGame("SnakeError: No loop in BodyView (%d)", bodyview); if (Room.ObjectCount < 1) AbortGame("SnakeError: Not enough objects in the room to start (%d, needs at least 1)", Room.ObjectCount); Snake.Clear(); if (delay < 1) delay = 1; if (direction != eKeyUpArrow && direction != eKeyDownArrow && direction != eKeyLeftArrow && direction != eKeyRightArrow) direction = eKeyRightArrow; Snake.set_HeadView(headview); Snake.set_BodyView(bodyview); if (tailview < 1) Snake.set_TailView(bodyview); else Snake.set_TailView(tailview); ViewFrame* frame; int n = 0; while (n < Game.GetLoopCountForView(headview)) { if (Game.GetFrameCountForLoop(headview, n) > 0) { frame = Game.GetViewFrame(headview, n, 0); n = Game.GetLoopCountForView(headview); } n++; } DynamicSprite* head = DynamicSprite.CreateFromExistingSprite(frame.Graphic); Width = head.Width; Height = head.Height; Length = 0; Dir = direction; Delay = delay; Snake.Start(); SurfaceSprite = DynamicSprite.Create(Room.Width, Room.Height); Snake.set_BodyObject(object[0]); object[IdObject].Graphic = SurfaceSprite.Graphic; }
int ToAdd; static int Snake::get_Queue() { return ToAdd; } static int Snake::set_Queue(int n) { if (n < 0) n = 0; ToAdd = n; } static function Snake::Pop(int n) { if ((n < 1) || (n > Length)) return; Length -= n; }
// Retourne l'image correspondant à la direction voulue DynamicSprite* GetSpriteForLoop(int view, int loop, int frame, int ref[]) { // Si on vise une diagonale "enrichie" if (loop > 7) { // On récupère l'image sur laquelle effecuter une rotation (8 > 5, 9 > 7, 10 > 7, 11 > 5) DynamicSprite* sprite = GetSpriteForLoop(view, 5+2*(loop==9||loop==10), frame, ref); // On tourne de 90 pour 8 et 9, de 270 pour 10 et 11 sprite.Rotate(90+(180*(loop==10||loop==11))); return sprite; } // On récupère la vignette de la séquence de référence ViewFrame* tmp_frame = Game.GetViewFrame(view, ref[loop], frame%Game.GetFrameCountForLoop(view, ref[loop])); // On crée une image correspondant DynamicSprite* sprite = DynamicSprite.CreateFromExistingSprite(tmp_frame.Graphic); // Si la vignette est inversée, on inverse l'image if (tmp_frame.Flipped) sprite.Flip(eFlipLeftToRight); // Si la séquence visée correspond à la séquence de référence, tout va bien if (ref[loop] == loop) return sprite; // Si on vise une séquence orthogonale if (loop < 4) { int diff = loop - ref[loop]; if (!(diff%2)) diff += (diff/diff); // On inverse la droite et le haut else if (!(diff%3)) diff -= (diff/diff); // pour trier en cadran horaire sprite.Rotate(diff*90); // On retourne en conséquence } // Si on vise une séquence diagonale en se basant sur une séquence orthogonale else if (ref[loop] < 4) { int wanted; // On va déterminer quelle séquence orthogonale on veut if (loop < 8) wanted = 1 + (loop<6); // 1 ou 2 si vers diagonale-gauche ou diagonale-droite else wanted = 3 * (loop==9||loop==11); // 0 ou 3 si vers diagonale-bas ou diagonale-haut // On récupère l'image voulue (on est assuré que wanted < 4, donc pas de récursivité infinie) sprite = GetSpriteForLoop(view, wanted, frame, ref); } // Si on vise une séquence diagonale en se basant sur une autre séquence diagonale else if (loop < 8) { // Si on a 4 et 5 ou 6 et 7 if (((ref[loop] == 4 && loop == 5) || (loop == 4 && ref[loop] == 5)) || ((ref[loop] == 6 && loop == 7) || (loop == 6 && ref[loop] == 7))) sprite.Flip(eFlipUpsideDown); // Si on a 5 et 6 ou 4 et 7 else if (((ref[loop] == 5 && loop == 6) || (loop == 5 && ref[loop] == 6)) || ((ref[loop] == 4 && loop == 7) || (loop == 4 && ref[loop] == 7))) sprite.Rotate(180); // Si on a 4 et 6 ou 5 et 7 else if (((ref[loop] == 4 && loop == 6) || (loop == 4 && ref[loop] == 6)) || ((ref[loop] == 5 && loop == 7) || (loop == 5 && ref[loop] == 7))) sprite.Flip(eFlipLeftToRight); } // On retourne l'image en question return sprite; }
function DrawBody(this DynamicSprite*) { int n = 0; DrawingSurface* surf = this.GetDrawingSurface(); surf.Clear(); while (n < Length) { int loop = Loop[n]; if ((Length > 1) && (n != Length-1)) { int from = Loop[n+1]; if (Loop[n] == 2) { // Bas-droite if (from == 0) loop = 4; // Haut-droite else if (from == 3) loop = 5; } else if (Loop[n] == 1) { // Bas-gauche if (from == 0) loop = 6; // Haut-gauche else if (from == 3) loop = 7; } else if (from == 2) { // Droite-bas if (Loop[n] == 0) loop = 8; // Droite-haut else if (Loop[n] == 3) loop = 9; } else if (from == 1) { // Gauche-bas if (Loop[n] == 0) loop = 10; // Gauche-haut else if (Loop[n] == 3) loop = 11; } } DynamicSprite* sprite; if (n+1 == Length) sprite = GetSpriteForLoop(TailView, loop, player.Frame, TailLoop); else sprite = GetSpriteForLoop(BodyView, loop, player.Frame, BodyLoop); surf.DrawImage(X[n], Y[n], sprite.Graphic); sprite.Delete(); n++; } surf.Release(); }
int cycle, playerframe; function repeatedly_execute() { if (player.Room == SnakeRoom) { if (Delay && !cycle) { if (Length > 0 || ToAdd > 0) { if (ToAdd > 0) { X = ArrayInt_Add(X, Length, player.x-Width/2); Y = ArrayInt_Add(Y, Length, player.y-Height); Loop = ArrayInt_Add(Loop, Length, player.Loop); Length++; } int n = Length-1, x_offset = 0, y_offset = 0; while (n >= 0) { if (n > 0) { X[n] = X[n-1]; Y[n] = Y[n-1]; Loop[n] = Loop[n-1]; } else if (n == 0) { X[0] = player.x-Width/2; Y[0] = player.y-Height; Loop[0] = player.Loop; } n--; } if (ToAdd > 0) ToAdd--; } // Gestion du changement (éventuel) de direction int diff; while ((KeySize > 0) && ((diff < 3) || (diff > 7))) { diff = Dir-Keys[0]; if (diff < 0) diff = -1*diff; if ((diff == 3) || (diff == 5)) Dir = Keys[0]; int n = 1; while (n < KeySize) { Keys[n-1] = Keys[n]; n++; } KeySize--; } if (Dir == eKeyLeftArrow) { player.x -= Width; if (Game.GetLoopCountForView(player.View) > 0 && Game.GetFrameCountForLoop(player.View, 1) > 0) player.Loop = 1; } else if (Dir == eKeyRightArrow) { player.x += Width; if (Game.GetLoopCountForView(player.View) > 1 && Game.GetFrameCountForLoop(player.View, 2) > 0) player.Loop = 2; } else if (Dir == eKeyUpArrow) { player.y -= Height; if (Game.GetLoopCountForView(player.View) > 2 && Game.GetFrameCountForLoop(player.View, 3) > 0) player.Loop = 3; } else if (Dir == eKeyDownArrow) { player.y += Height; if (Game.GetFrameCountForLoop(player.View, 0) > 0) player.Loop = 0; } // On remet à jour puisqu'on a changé entre-temps if (Length > 0) Loop[0] = player.Loop; if (player.x+(Width/2) > AREA_RIGHT) player.x = AREA_LEFT+Width/2; if (player.y>AREA_BOTTOM) player.y = AREA_TOP+Height; if (player.x-(Width/2) < AREA_LEFT) player.x = AREA_RIGHT-Width/2; if (player.y-Height < AREA_TOP) player.y = AREA_BOTTOM; player.x -= (player.x-Width/2-AREA_LEFT)%Width; player.y -= (player.y+Height-AREA_TOP)%Height; SurfaceSprite.DrawBody(); int i = 0; while (i < Length) { if (player.x == X[i]+Width/2 && player.y-Height == Y[i]) CallRoomScript(SNAKE_SELF_BITTEN); i++; } if (!GetWalkableAreaAt(player.x-GetViewportX(), player.y-GetViewportY())) CallRoomScript(SNAKE_WALL_HIT); i = 1; while (i < Room.ObjectCount) { if (AreThingsOverlapping(player.ID,1000+object[i].ID)) { IdBitten = object[i].ID; CallRoomScript(SNAKE_OBJECT_BITTEN); } i++; } } if (Delay) { cycle++; cycle = cycle % Delay; } if (playerframe != player.Frame) { playerframe = player.Frame; SurfaceSprite.DrawBody(); } } }
function on_key_press(eKeyCode keycode) { if ((player.Room == SnakeRoom) && (keycode == eKeyUpArrow || keycode == eKeyDownArrow || keycode == eKeyLeftArrow || keycode == eKeyRightArrow)) { Keys = ArrayInt_Add(Keys, KeySize, keycode); KeySize++; } }
function on_mouse_click(MouseButton button) { if (SnakeRoom == player.Room) ClaimEvent(); }
function on_event(EventType event, int data) { if ((event == eEventLeaveRoom) && (player.Room == SnakeRoom)) Snake.Clear(); } Liens :Installation :- Téléchargez le module et importez-le sous AGS ; ou bien créez vous-même un nouveau script et collez-y respectivement l'en-tête Snake.ash et le corps Snake.asc.
- Créez une nouvelle pièce et importez l'image de fond qui contient la surface sur laquelle se déplacera votre serpent.
- Dessinez des zones de déplacement (walkable area) là où le serpent peut se déplacer (c'est-à-dire qu'il ne faut pas de zone de déplacement là où il y a des murs/obstacles).
- Placez les bords (edges) de la pièce pour encadrer la zone de déplacement du serpent (utile lorsque, comme dans le projet de démonstration, le fond est plus grand que la zone de déplacement)
- Créez un premier objet "vide" : cet objet d'indice 0 sera utilisé par le moteur du Snake (modifiable par une simple commande de script si vous le souhaitez).
- Dans la fonction Before Fade In de la pièce, placez la commande suivante :
- Code:
-
Snake.Init(TETE, CORPS, QUEUE); où TETE correspond à la vue d'animation de la tête du serpent, CORPS à la vue d'animation des parties de son corps et QUEUE à la vue d'animation de sa queue (dernière partie du corps, si vous ne passez pas ce paramètre le serpent utilisera la vue du corps pour la queue).
- Voilà, vous pouvez d'ores et déjà jouer !
Gérer l'auto-morsure :- Spoiler:
À la fin du script de votre pièce, ajoutez le code suivant : - Code:
-
function on_call(int evenement) { if (evenement == SNAKE_SELF_BITTEN) { Snake.Stop(); // On arrête le serpent Display("Perdu l'autophage !"); // On affiche un message avec un mot compliqué QuitGame(1); // On propose de quitter le jeu } } Gérer la collision d'obstacles :- Spoiler:
Dans la fonction on_call créée précédemment, ajoutez la condition suivante : - Code:
-
if (evenement == SNAKE_WALL_HIT) { Snake.Stop(); // On arrête le serpent Display("Boum !"); // On affiche un message QuitGame(1); // On propose de quitter le jeu } Ajouter des objets (une pomme par exemple) :- Spoiler:
- Créez un nouvel objet dans votre pièce (je supposerai que vous l'avez nommé oPomme)
- À la suite de la commande Snake.Init dans la fonction Before Fade In de la pièce, placez la commande suivante :
- Code:
-
Snake.PlaceObjectOnRandomFreeCoordinates(oPomme); - Dans la fonction on_call créée précédemment, ajoutez la condition suivante :
- Code:
-
if (evenement == SNAKE_OBJECT_BITTEN) { if (Snake.BittenObject == oPomme) { Display("Miam !"); // On affiche un message Snake.Queue++; // On ajoute une partie au corps Snake.PlaceObjectOnRandomFreeCoordinates(oPomme); // On replace la pomme ailleurs } } - Ajoutez simplement une condition sur Snake.BittenObject pour chaque objet que vous voulez utiliser. Libre à vous d'y insérer les effets que vous voulez.
Note sur les vues d'animation :- Spoiler:
- Les dimensions de chaque partie du corps du serpent (tête et queue comprises) sont définies comme celles de l'image dans la première vignette disponible dans la vue d'animation de la tête. Par soucis d'harmonisation, utilisez des images de même dimension pour toutes les vignettes de toutes les vues d'animation du serpent.
- Vous n'êtes pas obligé de créer toutes les séquences pour le corps et pour la queue. Si vous fournissez une seule séquence, les autres seront générées automatiquement (par rotation).
- Vous pouvez définir des diagonales pour les parties du corps du serpent. Pour cela, utilisez les séquences 4 à 7. Là encore, vous n'êtes pas obligé de créer toutes les séquences, le moteur pouvant générer les autres depuis une seule.
Vous pouvez donc fournir uniquement la séquence 0 pour la queue et uniquement les séquences 0 et 4 pour le corps.
- Malheureusement, il est actuellement nécessaire de fournir les quatre séquences (bas, gauche, droite, haut) pour la tête du personnage.
Liste des commandes et propriétés :- Spoiler:
Toutes ces commandes et propriétés sont statiques, c'est-à-dire que vous devez toutes les faire précéder de Snake. dans le script. -
- Code:
-
bool PlaceObjectOnRandomFreeCoordinates(Object* objet) Place l'OBJET de façon aléatoire sur les zones de déplacement de la pièce. Retourne true si l'opération a été un succès, false si aucune zone libre n'a été trouvée.
-
- Code:
-
function Init(int vue_tete, int vue_corps, optional int vue_queue, optional int delai, optional eKeyCode direction) Convertit le personnage joueur en serpent en utilisant VUE_TETE pour l'animer et VUE_CORPS pour animer le corps du serpent et commence le déplacement. Si vous passez VUE_QUEUE, ce sera utilisé pour animer la dernière partie du corps du serpent. Si vous passez aussi DELAI, cela correspondra au nombre de cycles qu'il faut pour que le serpent se déplace d'une case. Si vous passez aussi DIRECTION (valeurs possibles : eKeyDownArrow, eKeyLeftArrow, eKeyRightArrow, eKeyUpArrow), le serpent commencera son déplacement dans cette direction.
-
- Code:
-
function Pop(optional int nombre) Enlève une partie du corps du serpent. Optionnellement, vous pouvez spécifier d'enlever plutôt NOMBRE parties.
-
- Code:
-
function Start() (Re)lance le déplacement du serpent.
-
- Code:
-
function Stop() Stoppe le déplacement du serpent.
-
- Code:
-
function Clear() Supprime le serpent, redonne son apparence normale au personnage joueur et redonne le contrôle normal (souris et clavier) au joueur.
-
- Code:
-
int Queue // (propriété) Retourne/Définit le nombre de parties à prochainement ajouter au serpent.
-
- Code:
-
int HeadView // (propriété) Retourne/Définit la vue utilisée pour animer la tête du serpent.
-
- Code:
-
int BodyView // (propriété) Retourne/Définit la vue utilisée pour animer les partie du corps du serpent.
-
- Code:
-
int TailView // (propriété) Retourne/Définit la vue utilisée pour animer la queue du serpent.
-
- Code:
-
int Delay // (propriété) Retourne/Définit le nombre de cycles de jeu entre chaque déplacement du serpent.
-
- Code:
-
Object* BodyObject // (propriété) Retourne/Définit l'objet de la pièce utilisé pour dessiner les parties du corps du serpent.
-
- Code:
-
readonly Object* BittenObject // (propriété) Retourne le dernier objet mordu par le serpent.
-
- Code:
-
readonly int Length // (propriété) Retourne le nombre de parties du corps (tête exclue) que possède le serpent.
-
- Code:
-
readonly int Height // (propriété) Retourne la hauteur en pixels correspondant à une partie du serpent
-
- Code:
-
readonly int Width // (propriété) Retourne la largeur en pixels correspondant à une partie du serpent
_________________ Ga is Ga Vous pouvez consulter l' aide d'AGS 3.2 en français et contribuer à la traduction et à l'amélioration si le cœur vous en dit ! |
|
Kitai Délégué de la tasse bleue
Nombre de messages : 2907
Date d'inscription : 01/08/2006
| Sujet: Re: Module Snake (AGS 3.2.1 et supérieures) Jeu 6 Juin 2013 - 17:00 | |
| - Billbis a écrit:
- Y a t'il moyen de rendre l'animation plus fluide (faire des transitions, au lieux de sauts brutaux de cases) ?
Hahaha (rire jaune)... C'est justement l'aspect qui fait que mon ancien "module" était tout crade La gestion "discrète", case par case, est facile : on se contente de déplacer la tête vers une nouvelle case puis de "remonter" les coordonnées des parties une par une. La gestion continue est moins évidente : il faut mémoriser toutes les coordonnées de bifurcation pour en faire des points de destination successifs de toutes les parties du corps. C'est loin d'être infaisable (à vrai dire c'était même fonctionnel sur ce que j'avais fait avant), mais le rendu n'est pas toujours tip-top : les parties qui vont dans des directions différentes se chevauchent et si les images ne sont pas carrées, alors ça fait vraiment bizarre puisqu'un bord va en dépasser un autre avant d'atteindre le point de bifurcation. Si tu as une idée pour implémenter ça ou si tu as envie de t'y attaquer directement, je ne dis pas non _________________ Ga is Ga Vous pouvez consulter l' aide d'AGS 3.2 en français et contribuer à la traduction et à l'amélioration si le cœur vous en dit ! |
|