Planète Casio - Vos tutoriels et astuces - Flux RSS http://www.planet-casio.com Programmes Casio, Jeux, Cours pour Calculatrices Casio fr-FR https://www.planet-casio.com/images/logo.gif Planète Casio - Vos tutoriels et astuces - Flux RSS http://www.planet-casio.com 55 50 Programmes Casio, Jeux, Cours pour Calculatrices Casio. Thu, 07 Nov 2024 19:04:13 GMT Thu, 07 Nov 2024 19:04:13 GMT contact@planet-casio.com (Planet Casio) contact@planet-casio.com (Planet Casio) 5 Guide for using Cahute and fxlink on WSL 2 https://www.planet-casio.com/Fr/forums/topic17875--.html This tutorial guides you to enable USB communication in WSL 2 for Cahute and fxlink, which is not supported natively by WSL as of this writing (updated 2024-11-6). Pre-requisites As the title suggests, this guide is intended for developers using WSL 2. This is because `usbipd-win` and `WSLg` do not support WSL 1 distros. It is also not applicable to Windows on ARM computers as the author of `usbipd-win` has no plans to support ARM64, although there is a complicated and non-production solution for ARM64 PCs. For Windows 10 users, you must be running version 21H2 or higher, which is the minimum requirement for `WSLg` to work. Setting up WSL 2 If you are new to WSL, open PowerShell as administrator and execute the following commands to enable it: dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart Restart your PC to apply these changes. The latest release of WSL 2 (v2.3.24) does not come with USB mass storage modules. Only pre-releases v2.3.11-2.3.14 provide them for now. Set the default install version for new distros to WSL 2: wsl --set-default-version 2 You can type `wsl --list --online` and select your preferred distro from the list by `wsl --install <distro> --web-download`, or install other third-party WSL distros like ArchWSL. The guide will be using Ubuntu 24.04 LTS for demonstration. Installing Cahute and fxlink Make sure to `sudo apt update && sudo apt upgrade` to have the latest libraries before going through all these instructions. Go to the README of GiteaPC and install it. Since we will be using the full version of fxlink, install the following dependencies: % sudo apt-get install cmake python3-pil libusb-1.0-0-dev libudev-dev libsdl2-dev libpng-dev libncurses-dev libudisks2-dev libglib2.0-dev Then continue with the remaining instructions as usual. Lastly, follow these instructions to install Cahute. Instructions for using `usbipd-win` On the Windows host, go to the release page for the `usbipd-win` project and download the latest .msi installer. After installing `usbipd-win`, connect your calculator with USB. Then open PowerShell as administrator and run `usbipd list`. The list should include one of the following information: 07cf:6101 CESG502 07cf:6102 Casio MassStorage Device 07cf:6103 USB Mass Storage Device Copy its bus ID and execute the following commands while the WSL instance is running: usbipd bind -b <calculator-busid> usbipd attach -w -b <calculator-busid> Note that for calculators that support USB Flash mode, you have to repeat the bind command for each connection mode as they have different `PID` as shown above. To let your calculator always connect to WSL instance instead of the host, add `-a` flag after `usbipd attach`. This will start endless attach loop for the calculator connection. For calculators that use CESG502, type `p7 info` in WSL terminal to confirm that Cahute can detect the calculator connection. If your calculator is in USB Flash mode, enter `lsblk` to see if its storage appears under `sdx` directory. Possible issues and workarounds 1. If you have installed `USBPcap` in Windows (which is used for debugging with Cahute), `usbipd` will prompt you: usbipd: warning: USB filter 'USBPcap' is known to be incompatible with this software; 'bind --force' will be required. If you prefer not to force bind your calculator to WSL instance, do not use both components simultaneously. 2. If `usbipd-win` cannot automatically load `vhci_hcd` module, edit the module config file: % sudo nano /etc/modules-load.d/modules.conf Add `vhci_hcd` module at a new line like this: # /etc/modules is obsolete and has been replaced by /etc/modules-load.d/. # Please see modules-load.d(5) and modprobe.d(5) for details. # # Updating this file still works, but it is undocumented and unsupported. vhci_hcd Save the config file and restart your WSL session. 3. Since fxlink is installed in your home directory, `fxsdk build-cg -s` may not work properly as it requires sudo privileges. In this case, execute the following workaround in WSL terminal: % which fxlink Copy its path and enter the following command at your fxSDK project directory as sudo: % sudo /full/path/to/fxlink -sw <your-addin>.g3a Another way (but not recommended) is to use root account and install fxSDK there instead. 4. If `p7screen` prompts you: Segmentation fault (core dumped) This is a bug that has been upstreamed to libsdl's bug tracker (thanks Cakeisalie5 for the report). To work around it, create a `WSLg` config file: % sudo nano /etc/tmpfiles.d/wslg.conf Save the config file with the following content and restart your WSL session: # Type Path Mode UID GID Age Argument L+ /tmp/.X11-unix - - - - /mnt/wslg/.X11-unix Sun, 15 Sep 2024 20:32:52 +0200 Tutoriel d'utilisation de libMicrofx https://www.planet-casio.com/Fr/forums/topic17848--.html Pour que le tutoriel soit plus visible que dans le repo de mon jeu de pong ( https://git.planet-casio.com/mibi88/pong/src/branch/master/TUTORIAL_fr.md ), je le poste ici aussi. J'ai utilisé https://rtxa.github.io/mdtobb/ pour la conversion du markdown vers du BBCode. J'ai réécrit la table à la main. J'ai aussi réécrit les listes. Tutoriel d'utilisation de libMicrofx Dans ce tutoriel nous allons coder un jeu de pong en C avec libMicrofx. Préréquis Des connaissances solides du C sont requises. Pour pouvoir suivre ce tutoriel, vous avez besoin du fxsdk, car libMicrofx réquis sh-elf-gcc etc. et fxgxa. Vous avez donc besoin d'un système d'exploitation UNIX-like compatible avec celui-ci. Vous avez bien sûr besoin d'une calculatrice CASIO monochrome sh-4 pour pouvoir tester l'add-in. Quelques infos utiles Le coin en haut à gauche est l'origine du repère. Le pixel en haut à gauche est aux coordonnées 0;0 et le pixel en bas à droite aux coordonnées 127;63, car l'écran fait 128x64 pixels. Création du projet Clonez le dépot libMicrofx (branche master). Faites une copie du dossier template, c'est un exemple de projet. Renommez votre copie de ce dossier avec le nom de votre nouveau projet (par exemple: pong). Lancez make dans ce dossier. Si vous n'avez pas d'erreurs et obtenez un add-in fonctionnel, vous pouvez continuer. Dans le dossier du projet, crééz un dossier assets pour y mettre les graphismes, ainsi qu'un dossier inc pour les headers. Structure du projet Nous allons structurer le projet comme ceci: main.c Lancement du jeu et boucle du jeu. game.c et game.h logique du jeu et rendu. game.h contiendra toutes les structures. ball.c et ball.h mouvement, collisions et rendu de la balle. paddle.c et paddle.h gestion des collisions entre la balle et la raquette, mouvement et rendu de la raquette. fixed.h Calculs à virgule fixe (si vous ne savez pas ce que c'est, c'est pas grave, j'en parlerais plus tard). Créez ces fichiers, les fichiers .c dans src et les fichier .h dans inc. Dans assets, créez un écran de titre (title_img.png) pour le jeu. Il doit avoir une dimension de 128x64 (la taille de l'écran). Vous pouvez aussi prendre celui du dépôt du tutoriel. Pour pouvoir l'utiliser depuis le code, il faut le convertir en code source. Vous pouvez utiliser sprite coder pour cela (https://tools.planet-casio.com/SpriteCoder/index.php). Vous devriez avoir un dossier comme ça : $ tree . ├── assets │ ├── title_img.c │ └── title_img.png ├── icon.png ├── inc │ ├── ball.h │ ├── fixed.h │ ├── game.h │ └── paddle.h ├── lib │ ├── fx98xx.ld │ ├── include │ │ └── microfx │ │ ├── ext │ │ │ ├── img.h │ │ │ └── strtools.h │ │ ├── keycodes.h │ │ └── microfx.h │ └── libMicrofx.a ├── Makefile └── src ├── ball.c ├── game.c ├── main.c └── paddle.c Makefile Ajoutez les fichiers .c à SRC, changez NAME et ajoutez -Wall -Wextra -Wpedantic à la ligne $(CC) -c $< -o $@ -Os -Ilib/include/ -std=c89 pour avoir plus de warnings, pour rendre le débogage plus facile. Ajoutez aussi -Iinc, pour ne pas avoir à écrire le chemin vers les headers. Si vous n'avez pas l'habitude du C ANSI enlevez -std=c89. Code Nous sommes enfin prêts à coder le jeu. - main.c, la boucle du jeu Nous allons commencer par faire la boucle du jeu. Pour que notre jeu soit agréable à jouer, nous allons limiter le nombre d'images par seconde à lequel il peut tourner, car la calculatrice est en mesure de l'exécuter très très vite. Nous allons quitter la boucle si EXIT est appuyé, ce que nous pouvons voir avec kcheck(KCEXIT). J'ai donc choisi de limiter le framerate à 50fps (cela vous rapelle peut être les jeux vidéos PAL des années 80). libMicrofx propose une variété de fonctions pour controller le temps. Nous allons utiliser treset, tgetticks et tiselapsed, car ils nous offrent la meilleure précision que libMicrofx est capable de fournir (1/128 secondes). Si vous avez besoin d'une plus grande précision, utilisez gint. tgetticks permet d'obtenir le temps écoulé depuis minuit. tiselapsed permet de savoir si X milisecondes se sont écoulés depuis un certain nombre de ticks. Nous allons nous en servir pour attendre de 20ms s'écoulent depuis le début de l'image. treset permet de réinitialiser le comptage du temps. On en a besoin, car au bout de 24h le temps est remis à zéro, ce qui pourrait faire crasher notre jeu. Nous allons donc d'abord réinitialiser le RTC et ensuite utiliser tgetticks pour obtenir le temps écoulé. Nous allons ensuite exécuter une image du jeu, puis, dans une boucle, utiliser tiselapsed pour attendre que 20ms soient écoulées. sclear efface l'écran et supdate met à jour l'écran donc mettez les entre tgetticks et tiselapsed. Essayez par vous même, et regardez ensuite mon implémentation. while(!kcheck(KCEXIT)){ /* Reset RTC to avoid a crash if it gets midnight. */ treset(); /* Get the current time. */ ticks = tgetticks(); /* Run a frame of the game. */ sclear(); /* A game frame. */ supdate(); /* Wait that 20ms are elapsed to let the game run at 50fps (PAL). */ while(!tiselapsed(ticks, 20)); } Testez. Si vous arrivez bien à quitter le jeu en appuyant sur EXIT, continuez. - game.h, les structures Maintenant que nous avons une boucle de jeu, nous pouvons commencer à coder le jeu en lui même. Nous allons créer trois structures, Game, qui rassemble toutes les informations sur le jeu, Ball, qui représente la balle, et Paddle qui représente une raquette. Nous allons mettre toutes les structures dans game.h (pour éviter le cross referencing). Nous allons commencer par créer la structure Paddle, c'est celle qui nous intéressera en premier. Celle pour la balle sera plus compliquée. Nous avons juste besoin de stocker la position de la raquette. /* A structure representing a paddle (used in paddle.c) */ typedef struct { /* The position of the paddle. */ int x; int y; } Paddle; Ensuite nous allons créer la structure Game avec deux raquettes. /* A structure storing all the informations about the game. */ typedef struct { /* The player's paddle */ Paddle paddle1; /* The computer's paddle */ Paddle paddle2; } Game; - paddle.c et paddle.h, gestion des raquettes. Maintenant que nous avons crée toutes les structures, il est temps de coder le mouvement et l'affichage des raquettes. - Initialisation d'une raquette. Dans paddle.h, écrivez, comme vous en avez sûrement l'habitude #ifndef PADDLE_H #define PADDLE_H #include <game.h> #endif Dans paddle.c, nous allons commencer par coder inclure paddle.h. Nous allons aussi inclure microfx/microfx.h. #include <paddle.h> #include <microfx/microfx.h> Nous allons créer une fonction paddle_init pour initialiser la raquette: void paddle_init(Paddle *paddle, int x) { /* TODO */ } Dans cette fonction, nous allons mettre paddle->y à SHEIGHT/2: SWIDTH et SHEIGHT sont définis par microfx/microfx.h et SWIDTH contient la largeur de l'écran. SHEIGHT contient sa hauteur. paddle->x sera égal à x. void paddle_ini(Paddle *paddle, int x) { paddle->x = x; paddle->y = SHEIGHT/2; } Mettez le prototype de cette fonction dans paddle.h. - Mouvement d'une raquette. Nous allons ensuite créer une fonction paddle_move pour pouvoir bouger la raquette. void paddle_move(Paddle *paddle) { /* TODO */ } Comme nous l'avons fait précédemment, nous allons utiliser kcheck pour récupérer les entrées clavier. KCUP est la flèche vers le haut et KCDOWN est la flèche vers le bas. Dans paddle.h nous allons définir la vitesse du paddle, ansi que sa taille. #define PADDLE_SPEED 1 #define PADDLE_WIDTH 3 #define PADDLE_HEIGHT 16 Nous n'allons pas bêtement incrémenter et décrémenter la position du paddle, car il ne doit pas sortir de l'écran. Mon implémentation : void paddle_move(Paddle *paddle) { /* Move the paddle up if the up arrow key is pressed. */ if(kcheck(KCUP)){ if(paddle->y-PADDLE_SPEED >= 0){ paddle->y -= PADDLE_SPEED; }else{ paddle->y = 0; } } /* Move the paddle down if the down arrow key is pressed. */ if(kcheck(KCDOWN)){ if(paddle->y+PADDLE_SPEED < SHEIGHT-PADDLE_HEIGHT){ paddle->y += PADDLE_SPEED; }else{ paddle->y = SHEIGHT-PADDLE_HEIGHT-1; } } } Mettez le prototype de cette fonction dans paddle.h. - Dessin d'une raquette Maintenant que notre raquette peut être initialisée et bougée, nous devons la dessiner. Nous allons dessiner un rectangle. Nous allons donc créer une fonction pour dessiner une raquette, et dessiner un rectangle à la position de la raquette, avec void srect(int x1, int y1, int x2, int y2); Mon implémentation : void paddle_draw(Paddle *paddle) { /* Draw a rectangle to represent the paddle. */ srect(paddle->x, paddle->y, paddle->x+PADDLE_WIDTH, paddle->y+PADDLE_HEIGHT); } Mettez le prototype de cette fonction dans paddle.h. - game.c et game.h, initialisation, rendu et logique Maintenant nous avons grand besoin de tester si tout ce que nous avons codé fonctionne. Nous allons donc créer trois fonctions: game_init pour initialiser le jeu. game_logic pour la logique du jeu. game_draw pour le rendu. - Initialisation du jeu. Dans game_init nous allons créer une première raquette avec une abscisse de 16 et une deuxième avec une abscisse de SWIDTH-16-PADDLE_WIDTH. Mon implémentation : void game_init(Game *game) { /* Initialize the paddles on the two sides of the screen. */ paddle_init(&game->paddle1, 16); paddle_init(&game->paddle2, SWIDTH-16-PADDLE_WIDTH); } Ajoutez le prototype de cette fonction à game.h. - Logique du jeu. Dans game_logic nous allons appeler paddle_move pour la première raquette. Mon implémentation : void game_logic(Game *game) { /* Let the player move his paddle. */ paddle_move(&game->paddle1); } Ajoutez le prototype de cette fonction à game.h. - Rendu Dans game_draw nous allons dessiner les raquettes avec paddle_draw. Mon implémentation : void game_draw(Game *game) { /* Draw the paddles. */ paddle_draw(&game->paddle1); paddle_draw(&game->paddle2); } Ajoutez le prototype de cette fonction à game.h. - main.c, appel des fonctions de game.c Avant de pouvoir tester, nous devons appeler toutes ces nouvelles fonctions. Créez une variable game, et au début de main, et initialisez la avec game_init. Ensuite, dans la boucle du jeu, après sclear et avant supdate, appelez game_logic puis game_draw. Mon implémentation de main : Game game; int main(void) { int ticks = 0; /* Wait for the user to release all the keys */ while(kisdown()); /* Initialize the game. */ game_init(&game); /* Main game loop. */ while(!kcheck(KCEXIT)){ /* Reset RTC to avoid a crash if it gets midnight. */ treset(); /* Get the current time. */ ticks = tgetticks(); /* Run a frame of the game. */ sclear(); game_logic(&game); game_draw(&game); supdate(); /* Wait that 20ms are elapsed to let the game run at 50fps (PAL). */ while(!tiselapsed(ticks, 20)); } /* Add-ins should return 1 on success (yes, it's weird). */ return 1; } - game.h et fixed.h, la balle et les nombres à virgule fixe. - Opérations à virgule fixe. Comme vous le savez sûrement déjà, l'écran de la calculatrice fait 128x64 pixels. Si l'on dessine un pixel à (0;0), et qu'on le déplace de 1 pixel sur l'axe des abscisses 50 fois par seconde, le pixel traverse l'écran en 2,56 secondes. C'est très rapide. Nous avons donc besoin de bouger le pixel de moins d'un pixel par image, mais comment faire ? C'est très simple: nous allons stocker sa position comme un nombre réel, et l'incrémenter d'un nombre plus petit que 1 (ex: 0,1), ce qui nous permet de gérer la vitesse de son déplacement plus précisément. Pour trouver ses coordonnés à l'écran, nous ne prendrons que les chiffres avant la virgule. Intuitivement, vous utiliseriez des floats mais la calculatrice n'a pas de FPU (une puce qui permet de faire des calculs à virgule flottante rapidement), donc tout est effectué sur le CPU ce qui rend les calculs avec des float très lent. De plus cela rend l'add-in plus volumineux (ce que nous ne voulons pas). Nous allons donc utiliser des entiers : nous allons garder quelque bits pour les chiffres après la virgule. C'est ce que l'on appelle les nombres à virgule fixe. Exemple: 19,2 comme entier 16 bit non signé avec une précision de 8 bits après la virgule : Avant la virgule Après la virgule 00010011 00110011 Si l'on reconvertit ce chiffre en float, on obtient environ 19,199, car nous avons perdu en précision, mais ce n'est pas grave dans un jeu vidéo (19,199 c'est quand même très proche de 19,2 !). Maintenant, codons quelque macros pour convertir des nombres en nombres à virgule fixe, et d'autres pour la multiplication et la division (nous pouvons faire les additions et les soustractions comme d'habitude). La conversion est très facile, il suffit d'effectuer des décalages de bits. /* The precision of the fixed point numbers. */ #define PRECISION 8 /* Convert a float to a fixed point number. */ #define TO_FIXED(num) (int)((num)*(1<<PRECISION)) /* Convert a fixed point number to an integer. */ #define TO_INT(num) ((num)>>PRECISION) Note : Je multiplie num par (1*PRECISION) pour pouvoir passer des floats à la macro. Les macros pour la multiplication et la division sont très simples, elles aussi : /* Multiply two fixed point numbers together. */ #define MUL(a, b) ((a*b)>>PRECISION) #define DIV(a, b) ((a<<PRECISION)/b) Nous allons finir par définir un type fixed_t, pour que notre code soit plus lisible : /* The fixed point type (to make the code easier to read). */ typedef int fixed_t; - Structure Ball. Dans game.h, nous pouvons donc créer une structure pour la balle, qui va contenir sa position, sa direction (sous forme de vecteur) et sa vitesse. /* The ball structure (used in ball.c). */ typedef struct { /* The position of the ball. */ fixed_t x; fixed_t y; /* How the ball position is increased on each frame. */ fixed_t x_inc; fixed_t y_inc; /* The speed of the ball. */ fixed_t speed; } Ball; Nous allons aussi l'ajouter à la structure Game: /* The ball */ Ball ball; - ball.c, ball.h et game.c, le dessin et le mouvement de la balle. - Initialisation de la balle. Avant de faire quoi que ce soit avec la balle, nous devons l'initialiser. Nous allons donc créer une fonction ball_init dans ball.c. Elle aura pour paramètres la direction et la vitesse de la balle. Elle sera placée au milieu de l'écran. Mon implémentation : void ball_init(Ball *ball, fixed_t x_inc, fixed_t y_inc, int speed) { /* Set the ball position to the center of the screen. */ ball->x = TO_FIXED(SWIDTH/2); ball->y = TO_FIXED(SHEIGHT/2); /* Set the increase on the x and y axis and the speed of the ball. */ ball->x_inc = x_inc; ball->y_inc = y_inc; ball->speed = speed; } Ajoutez le prototype à ball.h et appelez cette fonction depuis game_init, pour l'initialiser au lancement du jeu. - Dessin de la balle Mainenant que nous avons initialisé la balle, nous pouvons la dessiner. Nous allons tout simplement récupérer ses coordonnés à l'écran, et dessiner un rectangle de 3x3 que nous allons remplir (vu que l'intérieur ne fait qu'un pixel, nous allons utiliser spixel), pour qu'elle soit assez visible. void ball_draw(Ball *ball) { /* Get the position of the ball on screen. */ int screen_x = TO_INT(ball->x); int screen_y = TO_INT(ball->y); /* Draw a rectangle and fill it to make the ball more visible. */ srect(screen_x-1, screen_y-1, screen_x+1, screen_y+1); spixel(screen_x, screen_y, SBLACK); } Nous allons appeler cette fonction dans game_draw. Vérifiez que tout fonctionne, et si tout est bon, vous pouvez continuer. Sinon relisez votre code et le tutoriel. - Mouvement de la balle Mainenant que nous dessinons la balle, nous pouvons la faire bouger. Nous allons créer une nouvelle fonction char ball_move(Ball *ball); et commencer par calculer la position qu'elle atteindra à l'écran : /* The destination coordinates (on the screen) */ int dest_x = TO_INT(ball->x+MUL(ball->x_inc, ball->speed)); int dest_y = TO_INT(ball->y+MUL(ball->y_inc, ball->speed)); Nous allons tout d'abord vérifier si la balle sortira du côté droit ou gauche de l'écran, pour pouvoir donner un point au joueur qui a marqué. /* If dest_x > SWIDTH the player should get a point. */ if(dest_x > SWIDTH) return 1; /* If dest_x < 0 the computer should get a point. */ if(dest_x < 0) return 2; Nous pouvons aussi vérifier si elle doit rebondir sur les bords de l'écran, et nous allons la faire rebondir si besoin : /* Make the ball bounce on the sides of the screen. */ if(dest_y < 0){ ball->y_inc = -ball->y_inc; ball->y = TO_FIXED(0); } if(dest_y >= SHEIGHT){ ball->y_inc = -ball->y_inc; ball->y = TO_FIXED(SHEIGHT-1); } Nous allons finir par faire bouger la balle : /* Move the ball. */ ball->x += MUL(ball->x_inc, ball->speed); ball->y += MUL(ball->y_inc, ball->speed); Mon implémentation : char ball_move(Ball *ball) { /* The destination coordinates (on the screen) */ int dest_x = TO_INT(ball->x+MUL(ball->x_inc, ball->speed)); int dest_y = TO_INT(ball->y+MUL(ball->y_inc, ball->speed)); /* If dest_x > SWIDTH the player should get a point. */ if(dest_x > SWIDTH) return 1; /* If dest_x < 0 the computer should get a point. */ if(dest_x < 0) return 2; /* Make the ball bounce on the sides of the screen. */ if(dest_y < 0){ ball->y_inc = -ball->y_inc; ball->y = TO_FIXED(0); } if(dest_y >= SHEIGHT){ ball->y_inc = -ball->y_inc; ball->y = TO_FIXED(SHEIGHT-1); } /* Move the ball. */ ball->x += MUL(ball->x_inc, ball->speed); ball->y += MUL(ball->y_inc, ball->speed); return 0; } Appelez cette fonction depuis game_logic, et récupérez ce qu'elle retourne, on en aura besoin. Si la balle sort, réinitialisez la, pour pouvoir tester ce que nous venons de coder. - paddle.h et paddle.c, collisions avec la balle et paddle de l'ordinateur - Collision avec le paddle Maintenant que nous avons une balle qui bouge, nous devonss pouvoir vérifier lorsqu'elle touche le paddle. Pour la simplicité de ce tutoriel, je garderais les collisions très simple, mais n'hésitez pas à les améliorer (en vérifiant pour une collision des deux côtés du paddle). Créez une fonction paddle_will_collide_with_ball qui a pour paramètres la raquette et la balle. Elle vérifiera si, lors du prochain mouvement de la balle, elle entrera en collision avec le paddle. Nous allons commencer par calculer la position de la balle sur l'axe des abscisses après son mouvement : /* Get the position on the x axis the ball will go to. */ fixed_t dest_x = ball->x+MUL(ball->x_inc, ball->speed); Ensuite nous allons vérifier si la balle passera d'un côté à l'autre du côté gauche du paddle. Si c'est le cas, nous retournons 1 : /* Check if the ball would go through the paddle (on the X axis). */ if((ball->x > TO_FIXED(paddle->x) && dest_x <= TO_FIXED(paddle->x)) || (ball->x < TO_FIXED(paddle->x) && dest_x >= TO_FIXED(paddle->x))){ /* Check if the ball is on the paddle on the Y axis. */ if(ball->y >= TO_FIXED(paddle->y) && ball->y < TO_FIXED(paddle->y+PADDLE_HEIGHT)){ /* The ball will hit the paddle. */ return 1; } } Sinon, retournez 0. Appelez cette fonction depuis game_logic et inversez y_inc lors d'une collision pour pouvoir tester les collisions : if(paddle_will_collide_with_ball(&game->paddle1, &game->ball)){ game->ball.y_inc = -game->ball.y_inc; } if(paddle_will_collide_with_ball(&game->paddle2, &game->ball)){ game->ball.y_inc = -game->ball.y_inc; } - Mouvement du paddle de l'ordinateur Nous pouvons maintenant faire bouger le paddle de l'ordinateur en fonction de la position de la balle, maintenant que nous pouvons la renvoyer. Créez une fonction paddle_computer_move qui prend pour paramètres le paddle à bouger et la balle. Si la position à l'écran, sur l'axe des ordonnées, de la balle est plus petite que la position de la raquette, nous allons la monter, et si elle est plus grande nous allons la monter. Sinon nous allons rien faire vu que la raquette est en face de la balle. void paddle_computer_move(Paddle *paddle, Ball *ball) { /* Move the paddle up if the ball y position is smaller than the paddle y * position. */ if(TO_INT(ball->y) < paddle->y+PADDLE_HEIGHT/2){ if(paddle->y-PADDLE_SPEED >= 0){ paddle->y -= PADDLE_SPEED; }else{ paddle->y = 0; } }else if(TO_INT(ball->y) > paddle->y+PADDLE_HEIGHT/2){ /* Move the paddle down if the ball y position is bigger than the paddle * y position. */ if(paddle->y+PADDLE_SPEED < SHEIGHT-PADDLE_HEIGHT){ paddle->y += PADDLE_SPEED; }else{ paddle->y = SHEIGHT-PADDLE_HEIGHT-1; } } } - game.c et game.h, affichage des scores. - Initialisation et affichage des scores. Nous avons déjà (presque) un jeu fonctionnel, mais nous devons compter le score, pour pouvoir savoir qui a gagné et qui a perdu. Nous allons commencer par ajouter deux variables à la structure Game pour le stocker : /* The player's score */ unsigned char score1; /* The computer's score */ unsigned char score2; Nous allons ensuite l'initialiser dans game_init : /* Reset the score. */ game->score1 = 0; game->score2 = 0; Pour l'afficher, nous allons utiliser itoa pour convertir le score en chaine de caractères et stext pour l'afficher à l'écran. Les scores sont des unsigned char, donc un tableau de 4 caractères sera suffisant. Pour afficher le score, nous allons d'abord appeler itoa pour obtenir une chaine de caractères : itoa(game->score1, score_buffer); Ensuite nous pouvons l'afficher avec stext : stext(SWIDTH/2-8-5*3, 8, score_buffer, SBLACK); Les deux premiers arguments sont la position à laquelle afficher le texte, le troisième, le texte à afficher et le dernier argument est la couleur (SWHITE ou SBLACK). Nous allons faire de même pour le score de l'ordinateur ce qui nous donne /* Convert the score to a string. */ itoa(game->score1, score_buffer); /* Display the score */ stext(SWIDTH/2-8-5*3, 8, score_buffer, SBLACK); /* Do the same with the score of the computer. */ itoa(game->score2, score_buffer); stext(SWIDTH/2+8, 8, score_buffer, SBLACK); - Incrémentation des scores. Le nombre retourné par ball_move va enfin nous être utile ! Si il retourne 1 le joueur obtient un point, sinon c'est l'ordinateur qui obtient un point : move = ball_move(&game->ball); if(move == 1){ /* Player 1 got a point. */ game->score1++; /* Put the ball in the middle and send it to the computer */ ball_init(&game->ball, TO_FIXED(1), TO_FIXED(1), TO_FIXED(DEFAULT_SPEED)); } if(move == 2){ /* Player 2 got a point. */ game->score2++; /* Put the ball in the middle and send it to the player */ ball_init(&game->ball, TO_FIXED(-1), TO_FIXED(1), TO_FIXED(DEFAULT_SPEED)); } - game.c, écran de titre et écran de fin du jeu. - Mise en place Lorsque le joueur ou l'ordinateur atteint un certain score, il faut que la partie se termine et que le joueur puisse recommencer. Nous avons donc besoin d'ajouter un écran de fin. Nous allons aussi ajouter un écran de titre. Nous allons définir une énumération dans game.h qui liste les différents écrans : /* The screens. */ typedef enum { S_TITLE, S_INGAME, S_END, S_AMOUNT } State; Nous allons stocker l'écran actuel dans la structure Game : /* What we're currently displaying */ unsigned char state; Ensuite, nous allons l'initialiser dans game_init : /* Display the title screen. */ game->state = S_TITLE; Nous allons créer un grand switch dans game_logic et game_draw pour effectuer des actions différentes selon l'écran affiché. Nous allons mettre le code que nous y avons actuellement dans le cas S_INGAME. Dans game_logic : switch(game->state){ case S_TITLE: break; case S_INGAME: /* Let the player move his paddle. */ paddle_move(&game->paddle1); /* Let the computer move his paddle. */ paddle_computer_move(&game->paddle2, &game->ball); /* If the ball hits the player paddle, make it bounce off of it. */ if(paddle_will_collide_with_ball(&game->paddle1, &game->ball)){ game->ball.y_inc = -game->ball.y_inc; } /* If the ball hits the computer paddle, make it bounce off of it. */ if(paddle_will_collide_with_ball(&game->paddle2, &game->ball)){ game->ball.y_inc = -game->ball.y_inc; } move = ball_move(&game->ball); if(move == 1){ /* Player 1 got a point. */ game->score1++; /* Put the ball in the middle and send it to the computer */ ball_init(&game->ball, TO_FIXED(1), TO_FIXED(1), TO_FIXED(DEFAULT_SPEED)); } if(move == 2){ /* Player 2 got a point. */ game->score2++; /* Put the ball in the middle and send it to the player */ ball_init(&game->ball, TO_FIXED(-1), TO_FIXED(1), TO_FIXED(DEFAULT_SPEED)); } break; case S_END: break; default: /* This should never happen, but if it happens we go to the title * screen. */ game->state = S_TITLE; } Dans game_draw : switch(game->state){ case S_TITLE: break; case S_END: /* Coming soon */ case S_INGAME: /* Convert the score to a string. */ itoa(game->score1, score_buffer); /* Display the score */ stext(SWIDTH/2-8-5*3, 8, score_buffer, SBLACK); /* Do the same with the score of the computer. */ itoa(game->score2, score_buffer); stext(SWIDTH/2+8, 8, score_buffer, SBLACK); /* Draw the paddles. */ paddle_draw(&game->paddle1); paddle_draw(&game->paddle2); /* Draw the ball. */ ball_draw(&game->ball); default: /* This should never happen, but if it happens we go to the title * screen. */ game->state = S_TITLE; } Note : Je n'ai pas mis de break à la fin de S_END, et je l'ai mis avant S_INGAME dans game_draw pour toujours afficher l'écran de jeu lorsque la partie est terminé. On écrira juste du texte en plus ("GAME OVER" ou "YOU WIN"). - Écran de titre Nous allons commencer par ajouter l'écran de titre. Dans game_draw nous allons juste dessiner l'image de l'écran de titre : /* Show the title image. */ simage(0, 0, 128, 64, (unsigned char*)title_img, SNORMAL); Les deux premiers paramètres de simage indiquent la position à laquelle l'image doit être dessinée. Les deux prochains sont la taille de l'image, le cinquième un pointeur vers les données de l'image et le dernier comment l'image doit être dessinée : SNORMAL : On dessine le noir aussi bien que le blanc SINVERTED : On inverse les couleurs. STRANSP : le blanc est dessiné comme du noir et le noir n'est pas dessiné. Utile pour faire de la transparence. SNOWHITE : On ne dessine pas le blanc. SNOBLACK : On ne dessine pas le noir. Dans game_logic nous allons réinitialiser le jeu (pour tout remettre à zéro) et aller vers l'écran S_INGAME, si la touche SHIFT est pressée : /* We wait for the user to press shift to go to the in game screen. */ if(kcheck(KCSHIFT)){ /* Reset the game. */ game_init(game); game->state = S_INGAME; } - Écran de fin de la partie. Nous allons ensuite coder l'écran de fin de la partie. Dans game_draw, nous allons afficher "GAME OVER" ou "YOU WIN!" selon le score. Pour l'afficher au milieu, j'ai ajouté quelque defines en haut de game.c : #define WIN "YOU WIN!" #define GAME_OVER "GAME OVER" /* The position of the game over text. */ #define GAME_OVER_X (SWIDTH/2-5*sizeof(GAME_OVER)/2) /* The position of the win text. */ #define WIN_X (SWIDTH/2-5*sizeof(WIN)/2) Dans game_draw il suffit d'appeler stext selon le score obtenu par le joueur : if(game->score1 > game->score2){ /* The player won, so we display it. */ stext(WIN_X, END_Y, WIN, SBLACK); }else{ /* The player lost, so we display it. */ stext(GAME_OVER_X, END_Y, GAME_OVER, SBLACK); } Ensuite, dans game_logic nous allons attendre que SHIFT est pressé, puis attendre que cette touche soit relachée pour que l'écran de titre ne soit pas sauté. /* Check if shift is pressed. If shift is pressed, go to the title * screen. */ if(kcheck(KCSHIFT)){ game->state = S_TITLE; /* Wait for shift to be released, to avoid directly going to the * ingame screen after going to the title screen. */ while(kcheck(KCSHIFT)) csleep(); } Ensuite, nous devons aller à l'écran de fin au score maximal, lorsqu'on change le score : if(move == 1){ /* Player 1 got a point. */ game->score1++; /* Put the ball in the middle and send it to the computer */ ball_init(&game->ball, TO_FIXED(1), TO_FIXED(1), TO_FIXED(DEFAULT_SPEED)); /* If the player got the max score he won, so we go to the end screen. */ if(game->score1 >= MAX_SCORE) game->state = S_END; } if(move == 2){ /* Player 2 got a point. */ game->score2++; /* Put the ball in the middle and send it to the player */ ball_init(&game->ball, TO_FIXED(-1), TO_FIXED(1), TO_FIXED(DEFAULT_SPEED)); /* If the computer got the max score he won, so we go to the end screen. */ if(game->score2 >= MAX_SCORE) game->state = S_END; } - ball.c, ball.h et game.c, rebond de la balle sur la raquette. Pour l'instant, le rebond de la balle n'est pas très fidèle à celui de pong. Je trouve que faire l'interpolation linéaire entre y_inc = -1 et y_inc = 1 de la position de la balle par rapport à la raquette est assez fidèle à l'original. Nous allons donc créer une fonction ball_bounce qui a pour paramètre la balle et le paddle, dans laquelle nous allons calculer le nouveau y_inc comme je l'ai décrit ci-dessus. Mon implémentation : void ball_bounce(Ball *ball, Paddle *paddle) { /* Linear interpolation to calculate the new angle of the ball. */ int pos_on_the_paddle = ball->y-TO_FIXED(paddle->y); fixed_t angle_inc = DIV(TO_FIXED(ANGLE_RANGE), TO_FIXED(PADDLE_HEIGHT)); /* Fix the position of the ball on the paddle. */ if(pos_on_the_paddle < 0) pos_on_the_paddle = 0; if(pos_on_the_paddle > TO_FIXED(PADDLE_HEIGHT)){ pos_on_the_paddle = TO_FIXED(PADDLE_HEIGHT); } /* Perform the linear interpolation. */ ball->y_inc = TO_FIXED(ANGLE_TOP)+MUL(angle_inc, pos_on_the_paddle); ball->x_inc = -ball->x_inc; /* Increase the ball speed because we hit a paddle. */ if(ball->speed < TO_FIXED(MAX_SPEED)){ ball->speed += TO_FIXED(SPEED_INC); } } Ajoutez son prototype à ball.h et appelez la lors d'une collision, là où on a précedemment fait game->ball.y_inc = -game->ball.y_inc;. Améliorations possibles Améliorez les collisions, essayez de rendre le jeu le plus petit possible, cela vous entrainera bien. Vous pouvez aussi ajouter un mode deux joueurs ou un mode robot contre robot. Vous pouvez aussi rendre possible la séléction du score maximal (libMicrofx propose des fonctions de saisie de nombres et de texte). Étendre vos connaissances de libMicrofx Pour étendre vos connassances de libMicrofx je vous conseille de lire les headers dans lib/include/microfx. La plupart des fonctions sont dans microfx.h. Il y a des fonctions très utiles à la création de jeux vidéos dans les headers dans le dossier ext. C'est la fin de ce tutoriel Voilà ! Vous avez fini de lire ce tutoriel (ou presque). J'espère que ce tutoriel vous a plu et que vous vous amuserez bien avec libMicrofx ! Sun, 25 Aug 2024 13:22:10 +0200 Tutoriel d'optimisation et d'overclock pour le jeu OpenJazz (Graph 90+E) https://www.planet-casio.com/Fr/forums/topic17776--.html Attention ! Ceci n'est pas un tutoriel sur l'overclock (Si vous en voulez un, je vous conseille de commencer par ceci, C'est déjà un très bon début, à ma connaissance.). Je ne suis pas un fin connaisseur, juste un tripatouilleur. Ces réglages ont été faits pour le jeu Jazz Jackrabbit , je ne garantis pas qu'ils auront le même effet sur d'autres jeux. C'est tout, on peut commencer. Pour commencer, qu'est-ce que l'overclock. En gros, c'est une accélération matérielle ou logicielle des composants de l'appareil qu'on cible (RAM, CPU, GPU, VRAM, etc.). On peut aussi underclocker (ralentir la vitesse de ces composants). Ici, ce qu'on va faire, c'est d'augmenter les FPS (nombre d'images par secondes, ça joue énormément sur la fluidité) en accélérant le matériel. Je vous préviens tout de même : l'overclock peut diminuer la durée de vie de votre appareil. Voilà, vous êtes prévenu. Mais loverclock ne cassera pas votre calculatrice, ne vous en faites pas (du moins, il n'y a aucun cas répértorié de ce genre.). Une chose qu'il fera à coup sûr, par contre... C'est de consommer plus. C'est à dire que vos piles se videront plus vite . Donc, pour ce tutoriel, vous aurez besoin d'une Casio Graph 90+E, de OpenJazz d'installé sur votre calculatrice, et de Ptune 3, l'outil qui va vous permettre d'overclocker votre calculatrice. Tout est prêt ? Bien. Lançons Ptune3. Conseil : si vous voulez afficher les fps en jeu, une fois dans OpenJazz, appuyez sur F1. Si le compteur bug et affiche 1 en permanence, relancez le jeu. https://www.planet-casio.com/storage/forums/Overclock1-17776.bmp Ça, c'est le paramètre de base de votre calculatrice. En gros, il va faire tourner OpenJazz à 28/20 fps. Généralement 25. Si vous le modifiez et que vous souhaitez y revenir, appuyez sur la touche F1. https://www.planet-casio.com/storage/forums/Overclock3-197286.bmp Ça, c'est le paramètre F4. Il vous permettra de jouer à 35 fps, voir 30. c'est suffisant pour un jeu comme celui-là. https://www.planet-casio.com/storage/forums/Overclock2-197285.bmp Celui-là, c'est le paramètre F5. Il vous permettra de jouer à plus de 40 fps (47/48) constants, parfois même à 50. Mais si vous voulez aller plus loin, vous pouvez faire ceci : Appuyez sur la touche [^]. Cela pourrait vous faire gagner un ou deux FPS. Vous pouvez aussi monter la variable PLL, cela vous fera encore gagner quelques FPS. https://www.planet-casio.com/storage/forums/overclock4-197287.bmp Note: les paramètres F2 et F3 sont des paramètres d'underclock. Ils n'augmenteront pas vos fps. Voilà, cela vous donnera peut-être quelques bases pour faire vos réglages. ^^ Merci d'avoir lu, et bonne journée :) Thu, 30 May 2024 15:27:22 +0200 chiffres 1 à 9 en python extra sur graph 35+ EII https://www.planet-casio.com/Fr/forums/topic17771--.html Avec python extra je me suis amusé ce matin à coder les chiffres de 0 à 9 de 5 pixels de haut et 4 pixel de large. from gint import * c=[[[1,1,1,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,1,1,1]],[[0,0,0,1],[0,0,0,1],[0,0,0,1],[0,0,0,1],[0,0,0,1]],[[1,1,1,1],[0,0,0,1],[1,1,1,1],[1,0,0,0],[1,1,1,1]],[[1,1,1,1],[0,0,0,1],[0,1,1,1],[0,0,0,1],[1,1,1,1]],[[1,0,0,1],[1,0,0,1],[1,1,1,1],[0,0,0,1],[0,0,0,1]],[[1,1,1,1],[1,0,0,0],[1,1,1,1],[0,0,0,1],[1,1,1,1]],[[1,1,1,1],[1,0,0,0],[1,1,1,1],[1,0,0,1],[1,1,1,1]],[[1,1,1,1],[1,0,0,1],[0,0,0,1],[0,0,0,1],[0,0,0,1]],[[1,1,1,1],[1,0,0,1],[1,1,1,1],[1,0,0,1],[1,1,1,1]],[[1,1,1,1],[1,0,0,1],[1,1,1,1],[0,0,0,1],[1,1,1,1]]] dclear(0) for n in range(10): for y in range(5): for x in range(4): if c==1:dpixel(x+n*8+0,y,3) dupdate() k=getkey() voici ce que ça donne : https://imgur.com/yZ9EthZ.png ça n'a pas trop d''intérêt si ce n''est pour comprendre les listes... Sat, 18 May 2024 10:50:38 +0200 Coder un add-in en Python! https://www.planet-casio.com/Fr/forums/topic17719--.html Salut tout le monde :D , Comme on le sait tous, un add-in se fait coder en C ou en C++. Aujourd'hui j'ai découvert un site web qui permet de transformer un code python en un code C avec de l'IA! le site web est ici: https://www.codeconvert.ai/python-to-c-converter . Si vous utilisez gint, vous pouvez simplement mettre les fonctions Python comme ce que vous aurez fais avec Python Extra, et ensuite entré ces lignes au début du code: #include <gint/keyboard.h> #include <gint/display.h>Et c'est fais! Normalement les résultats sont très impressionnant et correct. Si il y a une erreur tout de même, vous pouvez toujours poster un commentaire ;) . Voilà voilà! Tuper Fri, 15 Mar 2024 18:37:37 +0100 Question en C https://www.planet-casio.com/Fr/forums/topic17613--.html Salut tout le monde! Je voulais savoir est ce que c'est possible de définir une variable dès la première ligne. Et est ce que on est obligé d'utiliser "void" car le projet initiale que le donne le sdk est un long paragraphe et je ne sais pas où commencer. Merci pour vos réponses ^^ Wed, 10 Jan 2024 09:33:09 +0100 Demande de conseil sur l'introduction d'une double condition dans un prog pour fx 8500 G https://www.planet-casio.com/Fr/forums/topic17586--.html SUR UNE FX 8500 G JE VOUDRAIS DANS UN PROGRAMME METTRE DEUX CONDITIONS POUR LANCER UNE BOUCLE: Soit M résultat d'un calcul A > 0 ET B > 0 entraîne GOTO 1 A > 0 ET B < 0 entraîne GOTO 2 A < 0 ET B < 0 entraîne GOTO 3 LBL 1 M LBL 2 M + 100 LBL 3 M + 200 Il n'y a pas de fonction AND prête à servir ans la FX 8500 G Je suis preneur de tout conseil, merci d'avance et bonne année. Tue, 02 Jan 2024 20:16:34 +0100 Peut on coder en C ou en C++ directement a partir de ta casio? https://www.planet-casio.com/Fr/forums/topic17540--.html Salut tous le monde :D Je suis nouveau ici mais je m'y connais un peu (Je sais débrider ma Casio en gragh 75, je sais codé sur Casiobasic, je connais le principe des adds-ins et je sais codé en python). Sinon, j'aimerais bien savoir si cela est possible de codé des adds-ins directement à partir de ma Casio. D'ailleurs j'avais une Casio graph 35+e je j'ai transformer en graqh 75. Merci Sat, 25 Nov 2023 14:27:21 +0100 Doubler son espace de stockage sur ces calculatrices Casio ! https://www.planet-casio.com/Fr/forums/topic17467--.html Le titre est un peu faux :sry: Bonjour cher membre de Planet Casio ! Vous en avez marre de voir le popup : Stockage Plein ? Eh ben j'ai la solution pour vous ! Par exemple vous voulez télécharger Clonelab mais il prend pratiquement tout l'espace de stockage : 46,08/60 ko. Eh ben au lieu de le télécharger dans @MainMem télécharger le plutôt dans SAVE-F : https://cdn.discordapp.com/attachments/818220009440280616/1163164174944305263/Untitled.png?ex=653e940d&is=652c1f0d&hm=067a24be3e8d0acc7236df742c2adb66f04b8f5207a1d73ea40e3d7856f12107& Dessin fait avec souris :oops: Maintenant que vous avez télécharger le fichier comment y accéder ? Premièrement diriger dans le gestionnaire de fichier : https://cdn.discordapp.com/attachments/818220009440280616/1163162086566809631/Menu-MEM.jpg?ex=653e921b&is=652c1d1b&hm=fd55ed0c9e61db479cc26c76a6081b40dbfb50aa6dbdc849c4f9094e1bbcb2aa& Puis aller dans la mémoire de stockage : https://cdn.discordapp.com/attachments/818220009440280616/1163162086298365992/MEM-STO.jpg?ex=653e921b&is=652c1d1b&hm=cafc318ac061e2a3c699743403a45ecb701909dfd549e0b465d1933a0143b0ee& Et pour finir aller dans le dossier SAVE-F et sélectionner le jeux auquel vous voulez jouez en appuyant sur F1 et puis appuyer sur F2 : https://cdn.discordapp.com/attachments/818220009440280616/1163162086042509312/COPI-FICH.jpg?ex=653e921b&is=652c1d1b&hm=db7665c310d39877be20a7d91cd26b5d1180363537f6fff3929c78b93ffcabb4& Bonne séance de jeux :lol: . Avantage : - Puisque vous mettez les fichier dans la mémoire de stockage vous avez beaucoup plus de place (logique :mmm:) Inconvénient : - Vous devez transférer les fichiers vers la mémoire principale pour pouvoir y accéder PS : Si le jeux que vous avez installer et jouable seulement en C.Basic ne le mettez pas dans SAVE-F sinon vous pourrez pas y accédez. Sun, 15 Oct 2023 19:38:55 +0200 Faire fonctionner Screen Receiver https://www.planet-casio.com/Fr/forums/topic17415--.html Bonjour, lorsque j'essaye d'utiliser Screen Receiver avec ma calculatrice Graph35+E avec une Os de Graph75, ça m'écrit "Réglez les parametres de screen receiver" et je ne sais pas comment faire. Pouvez-vous m'aider ? Fri, 28 Jul 2023 16:50:24 +0200