Exemple de moteur de jeux 2D en java

Pour tout ce qui est fan arts, homebrew, shooters codés à la main, rip de sprites, doujins et toute autre productions artistiques ou logicielles faites maison.
Répondre
Avatar de l’utilisateur
niluge
Radiant Silverpost
Messages : 1247
Inscription : 29 juin 2006, 15:29
Localisation : Above and beyond

J'en ai parlé dans l'annuaire, mais je préfère éviter de le polluer avec ça.

Voici donc le fichier jar d'exemple avec les sources associés :
Fichier Jar
Source

Edit : Je precise au cas ou. Un Jar est une archive java exécutable, il suffit de double cliquer pour lancer le jeux

Alors pour info :
- Oui c'est un plateformer, mais on s'en fous, c'est pour l'exemple
- Ce n'est pour le moment qu'une maquette qui me sert a tester quelques idées.
- Le programme n'est pas encore optimisé.
- Le code n'est pas encore correctement commenté, je sais.
- Même si je suis dev, c'est la première fois que je m'attaque a un jeu. Il y a donc certainement des maladresse. Merci de mes les préciser ^^.
- C'est la première fois que je pousse autant dans le java. Il y a certainement des redondances avec des classe java du SDK et quelques optimisation sur la partie graphique
- Il reste encore pas mal de petit truc a implémenter (contraintes sur la camera par exemple)

Optimisation possible et a envisager pour un shoot:
- le scrolling étant forcé, libéré la mémoire des fond qui sont déjà passé.
- Utiliser un thread pour charger à la volé les images nécessaire pour la suite (pas la peine de charger TOUTES les images du niveau )
- Ordonner les objets à afficher en fonction du sens de déroulement afin de limiter les calculs de collisions
ou ceux servant a déterminer les images à afficher (on ne calcul pas les collisions avec ce qui est en dehors de l'écrans et on ne cherche pas à afficher ce qui est derrière le premier objet non affiché)

- Ne pas hésiter a m'informer des problèmes que vous pourriez rencontrer avec mon exemple, ça m'aideras a debugger ^^
- Ne pas hésiter a me poser des questions si vous ne comprenez pas quelques chose dans mon code
- Ne pas hésiter a reprendre et triturer mon code si vous le voulez pour faire un jeux. Merci juste de me prévenir et de le préciser quelques part.

Critique, conseil et demandes bienvenues

Edit : Pour info, j'ai fait un petit test de charge consistant a afficher 1000 tiles de 32*32 en plus de ce qu'il y a déjà d'affiché et je suis toujours au dessus de 60image seconde sur mon macbook 2.26Ghz/2Go de Ram. Donc pour un shoot, je pense que c'est jouable ^^
Dernière modification par niluge le 29 mars 2010, 21:32, modifié 2 fois.
Risike
Dieu de la Borne
Messages : 1935
Inscription : 01 déc. 2009, 13:30
Localisation : Citoyen du monde, partisan d'un monde sans frontière

Pas mal ! Je suis impressionné que tu ais fais ça avec une JFrame !
La jeunesse n'est pas une période de la vie, mais un état d'esprit...
Avatar de l’utilisateur
niluge
Radiant Silverpost
Messages : 1247
Inscription : 29 juin 2006, 15:29
Localisation : Above and beyond

Merci, mais il n'y a rien de bien compliqué. Beaucoup d'info et quelques tuto sont disponible sur le site de sun en fait. Le truc c'est de bien fouiller, car ce n'est pas toujours facile de s'y retrouver.

Juste une précision. Pour le moment, ça tourne en fenêtré; mais il faut savoir qu'en java il est possible de modifier la résolution de l'écran et de passer en mode full screen exclusif (histoire de faire un VRAI jeu et d'écrire directement dans la mémoire video sans passer par le gestionnaire de fenêtre). Si je n'utilise pas cette méthode pour le moment c'est que si cette méthode fonctionne très bien sous windows (testé et approuvé ^^) elle semble poser quelques petits problèmes sur Mac. Merci l'universalité de Java :/ (même si sur ce coup je suspecte Mister Jobs de limiter les possibilité d'accès aux couches bas niveau de son OS )
yenshin
No-bullet mode
Messages : 36
Inscription : 03 sept. 2009, 15:03

yop bon j'ai matter le code du truc, pas tester encore tester.

remarques:

1. coding style:

dans l'ensemble c'est plutot bien coder, fait attention à certaine fonction qui sont plutot enorme, pense a bien découper. (ex:GetLayerCamShot, computeMove, OpenAnim).
Certains attribut des objets sont appeller sans this ou lenomdelaclasse.attribut, ce qui peut gener un peu a la comprehension.
attention a certaine indentation (bon ca vient peut etre de mon eclipse je sais pas)

2. architecture:

humm je pense que tu va galerer pour continuer ton jeux vu comment il est architecturer.
Par exemple pourquoi avoir un move dans player ? pourquoi ne pas utiliser une super classe, genre sprite auquel tu fais passé des arguments ?
je te conseillerai de faire des classes bien générique et de voir ce que tu as besoin pour ton jeu. Après il sera très facile de faire hérité les objets de tes classes génériques.

Du coup c'est un peu dommage tout ce melange. En tout ca si tu ne veux pas faire d'héritage, essaye de faire des packages plus explicite ca aidera a trouver koi utilise koi. (oui l'heritage n'est pas une solution ultime, mais être clair dans ce que tu vas/veux faire ca aide enormement)

par exemple tu pourrai regrouper toutes tes classes de layer entre elle, afin de montrer que ces des truc qui marche ensemble.

1 package par fonctionalité.
un peu comme ce que ta fait avec painter, dommage qu'on ne sache pas a quoi ca corresponde exactement

oui je sais ca n'as rien a voir avec une spécificité du jeux vidéo.

un ptit uml ? deja ca aide ^^

3. conclusion:

bon tout ça n'engage que moi chacun à son style. Mais essaye de savoir dès le départ ou tu veux arriver sinon tu ne vas jamais pouvoir avoir un code propre et tu va galerer a rajouter des features.
Faire un code qui marche rapidement c'est pas dur.
Faire un code qui marche et qui sois evolutif putain ca prend la tête mais au bout du compte tu avanceras 20 foi plus vite quant t'auras plus qu'a spécifier les niveaux les bonhumme et tout et tout.

edit:

ta un acces svn ?
j'aimerai bien suivre ton projet ca fait un parallele avec le mien en python

fin edit

Bon courage mec :)
Image
Avatar de l’utilisateur
niluge
Radiant Silverpost
Messages : 1247
Inscription : 29 juin 2006, 15:29
Localisation : Above and beyond

Merci ^^

Comme je l'ai dit, pour le moment ce n'est qu'une maquette ou j'implemente mes idées. Je veux être sur qu'elles soient validées avant d'aller plus loin. Donc ça ne m'etonne pas qu'il y a ai encore du boulot niveau design. Il vas falloir que je reflechisse a ça calmement. Surtout que j'ai déja, avec ce test, réunis les différents éléments qu'il me fallait (je vais faire juste un test sur le pixel perfect avant ^^)

Pour l'indentation, sur mon netbeans (Macosx) il n'y a pas de problème, surtout que je suis hyper pointilleux a ce niveau là.

Tu trouves serieusement mes méthodes énormes ? J'ai du trop m'habituer aux méthodes de 1000 lignes que je doit debugger au boulot je pense ^^. J'en vois bien une ou deux a degraisser, mais pour le reste je ne trouvais pas ça si énorme... Interessant.

Pour les packages, je suis tout a fait d'accord. Je pense que je vais faire un package GameSDK avec des sous packages pour classer tout ça. Et les classe dérivées specifique a mon jeu seront dans un autre package.

Pour SVN, vu que je developpe tout seul dans mon coin, je doit avouer ne pas avoir de compte. Mais je pense mettre en place un petit site internet de suivis avec peut être quelques tuto pour expliquer mes decouvertes

En tout cas merci d'avoir regardé mon code. Bossant toute la journée dedans, je sais a quelle point il n'est jamais facile de se plonger dans le code des autres
yenshin
No-bullet mode
Messages : 36
Inscription : 03 sept. 2009, 15:03

heu les fonctions enorme yen a 2 ou 3 rien de mechant donc tinkiete :)

après ouai moi j'aimerai bien voir ce que tu fais, c'est toujours mieux un svn (pour matter du code), que de telecharger la release a la con pas fini en debug je suis une endive.

mais bon ok après tu peux faire un svn gratos sur google, mais faut publier ton code en libre c'est le seul truc.

enfin voila

bonne journee
Image
Avatar de l’utilisateur
niluge
Radiant Silverpost
Messages : 1247
Inscription : 29 juin 2006, 15:29
Localisation : Above and beyond

Arghhhh gros coup de stress, un pote me dit ne pas voir le perso animé sur mon test (celui qu'on dirige). Pourriez vous me confirmer qu'il apparait bien chez vous svp ? Dire que le Java est sensé être "universel" :D

pour Info, ça ne marche pas chez lui sur un mac avec leopard. Chez moi sur snow leopard, aucun problème
chaos
Jeune Pad-awan
Messages : 60
Inscription : 23 mai 2009, 19:54

J'ai regardé vite fait le code (comme j'avais la flemme de lancer NetBeans), sa a l'air propre et commenté(par contre on switch sans arrêt français anglais c'est rigolo ^^). J'ai vue que tu utilisait la masse de Vector sans connaitre les tailles d'avance, bonjours les réalocations je te conseille plutôt des LinkedList.

Bon après je suis pas très pro JAVA pour les jeu car c'est un domaine ou on a besoin de gérer les ressources de la machine en temps réel, mais sa a le mérite d'être un langage simple a manipuler.

Par contre je trouve l'architecture un peu anarchique ^^, j'ai tendance a représenter les miennes sous formes d'arbres ou chaque nœud est un éléments (avec a la racines les sprites, sons ect... et a la tête mon gestionnaire de scène).

Un conseil aussi sur la boucle principal : éviter de tout décomposer en fonctions. Un appel de fonction c'est peut être peu gourmand mais 60 fois par seconde sa pèse quand trop de fonctions s'encapsule (faut pas non plus que cela devienne illisible avec un pavé de code enorme). C'est dommage d'ailleurs que l'on ne retrouve pas de fonctions inline en JAVA.
Avatar de l’utilisateur
niluge
Radiant Silverpost
Messages : 1247
Inscription : 29 juin 2006, 15:29
Localisation : Above and beyond

Allez hop, je remonte un peu ce thread. Comme je suis actuellement en inter-contrat, je m'amuse un peu a retoucher a mon moteur de jeu.

Je m'amuse a le tester en implémentant un peu a l'arrache un niveau de demo type shmup.
Pour le moment c'est très sommaire (pas beaucoup plus que ce que j'ai deja montré en fait niveau moteur de jeu, si ce n'est la gestion des collisions et une amélioration niveau scrolling parallaxe).

je posterais plus tard mon niveau de test (surtout pour tester les perf.très variable d'une archi a l'autre d'ailleurs. Nickel su mac osx, mais ça rame su windows les quelques fois ou j'ai testé... )

voici juste le code source de mon générateur de pattern. pas mal de truc a fignoler de-ci de-là. Mais le moteur de base fonctionne. Reste plus qu'a trouver les paramètres pour des pattern sympa :

Code : Tout sélectionner

import java.util.*;
import java.lang.*;
import java.awt.image.*;

import oolong.Sprite.*;

public class BulletGenerator {
    
    // table de cosinus et sinus pour les rotations
    private static float cos[];
    private static float sin[];
    
    private BufferedImage bulletImg;
    private Mask bulletMask;
    
    // Parametre du pattern
    public int xPos;
    public int yPos;
    public int nbBranch; // Nombre de branche
    public int spread;   // dispersion du tir
    public int shootByWave;  // Nombre de boulette par branche et par salve
    public int shootingFreq; // Nombre de frame entre deux shoot
    public int coolingFreq;  // Nombre de frame entre deux salve de tir
    public int rotationFreq; // Nombre de frame entre deux rotation du motif
    private int startAngle;   // Angle de depart du motif
    private int endAngle;     // Angle de fin du motif
    public int angleDelta;   // Variation entre chaque rotation
    public boolean loopToStartPosition; // reboucle au depart ou retour en arriere
    
    // Gestion d'une salve
    private boolean isShooting; // Est ce qu'on est en train de shooter
    private int nbShootDone;    // Nombre de shoot deja realisé
    private int framesSinceLastShoot;  // Combien de frame depuis le dernier shoot
    
    // Gestion de refroidissement :
    private int framesSinceStartCooling; // Nombre de frame écoulé depuis le debut de cette phase de refroidissement
    
    // Gestion des rotations
    private int framesSinceLastRotation;    
    private int currentAngle;
    
    
    public BulletGenerator (BufferedImage bulletImg, Mask bulletMask){
        
        // On initialise les tables de cos et sin si besoin
        if (cos == null || cos.length != 360){
            cos = new float[360];
            sin = new float[360];
            for (int i = 0; i < 360; i++){
                cos[i] = (float)Math.cos((2f*(float)i*Math.PI)/360f);
                sin[i] = (float)Math.sin((2f*(float)i*Math.PI)/360f);
            }
        }
        this.bulletImg = bulletImg;
        this.bulletMask = bulletMask;
        
        xPos = 780;
        yPos = 300;
        nbBranch = 7;
        spread = 21;
        shootByWave = 8;
        shootingFreq = 3;
        coolingFreq = 60;
        rotationFreq = 3;
        startAngle = angleMgmt(-45);
        endAngle = angleMgmt(45);
        angleDelta = 3;
        loopToStartPosition = false;
        
        isShooting = true;
        nbShootDone = 0;
        framesSinceLastShoot = 0;
        
        framesSinceStartCooling = 0;
        
        framesSinceLastRotation = 0;
        currentAngle = startAngle;
        
    }
  
    public void generate(LinkedList<Sprite> sprLst){
        
        int tmpAngle = 0;
        int i = 0;
        int angleBetween2Branche = 0;
        // Gestion d'une vague de tir
        if (isShooting == true || coolingFreq == 0){
            if (framesSinceLastShoot >= shootingFreq){
                // On tir comme des barges
                if (nbBranch > 1){
                    angleBetween2Branche = spread/(nbBranch-1);                
                    tmpAngle = angleMgmt(currentAngle - (spread/2));
                }
                else {
                    tmpAngle = angleMgmt(currentAngle);
                }
                
                for (i=0; i<nbBranch; i++){
                    
                    Bullet newBul = new Bullet (bulletImg,
                                                bulletMask,
                                                -4f*cos[tmpAngle],
                                                -4f*sin[tmpAngle]);
                    
                    newBul.setPosition(xPos, yPos);
                    sprLst.add(newBul); 
                    
                    tmpAngle = angleMgmt(tmpAngle + angleBetween2Branche); 
                }
                nbShootDone ++;
                
                if (nbShootDone >= shootByWave){
                    isShooting = false;
                    framesSinceStartCooling = 0;
                }
                
                framesSinceLastShoot = 0;
            }
            
            else{
                framesSinceLastShoot++;
            }
        }
        
        // Gestion du refroidissement
        else{
            framesSinceStartCooling ++;
            
            if (framesSinceStartCooling >= coolingFreq){
                isShooting = true;
                nbShootDone = 0;
                framesSinceLastShoot = shootingFreq;
            }
        }
        
        // Gestion de la frequence de rotation
        if (framesSinceLastRotation >= rotationFreq){
            
            framesSinceLastRotation = 0;
            currentAngle = angleMgmt(currentAngle + angleDelta);
            
            if (endAngle >= startAngle){
                if (currentAngle <= startAngle || currentAngle >= endAngle){
                    if (loopToStartPosition == true)
                        currentAngle = startAngle;
                    else{
                    angleDelta = -angleDelta;
                    }
                }
            }
            else{
                if (currentAngle <= startAngle && currentAngle >= endAngle){
                    if (loopToStartPosition == true)
                        currentAngle = startAngle;
                    else{
                        angleDelta = -angleDelta;                    
                    }
                }           
            }
        }
        else {
            framesSinceLastRotation ++;
        }
    }
    
    private int angleMgmt (int angle){       
        int result = angle%360;
      
        if ( result < 0)
            result = 360+result;
      
        return result;
    }   
}
le plus intéressant se trouvant dans la méthode generate. Comme c'est un générateur très générique, je me suis dit que ça pourrait en intéresser certains. Ne pas hésiter a me poser des questions ^^.
Avatar de l’utilisateur
Radigo
Counter Stop
Messages : 7567
Inscription : 22 mai 2003, 17:31
Localisation : Paris
Contact :

Ca rame ça ?

Je n'arrive pas à estimer le nombre de sprites affichés mais là tu pré-calcules tes sin/cos, tu manipules que du int et y'a que du bitmap. C'est vraiment bizarre.

En trollant je dirais que flash s'en sortirait mieux pour le coup...

Tu gères les collisions proprement qqpart ? Tu dégomme les sprites inutiles proprement ?

Tu peux tenter de faire un pool pour tes boulettes, ça m'a permis de balancer du gros pattern sans saccades sur un ancien projet.
"HYPER GAGE : 500%"
Image
Avatar de l’utilisateur
niluge
Radiant Silverpost
Messages : 1247
Inscription : 29 juin 2006, 15:29
Localisation : Above and beyond

Quand je dit que ça rame, c'est assez étonnamment sur quelques configs (en fait sur les quelques machines windows testés. Mais peut être est ce du a des machines virtuelles pourris).

Sur mon macbook (2.2Ghz dual core) ça marche nickel à 7ms par frame (hors garbage collector qui me fait bien suer. là je passe a 17ms. Et ça une fois toutes les 15-20 images). Le tout avec gestion des collisions(pixel perfect), et suppression des boulettes inutiles. resolution 800x600 avec 4 scrolling pas trop trop chargés.

Avec les paramètres de bases, je doit afficher 224boulettes par frames en moyenne.
Avatar de l’utilisateur
Radigo
Counter Stop
Messages : 7567
Inscription : 22 mai 2003, 17:31
Localisation : Paris
Contact :

Oui, la machine virtuelle Java m'a toujours saoulé, déjà en tant qu'utilisateur (je ne développe pas en Java). Ça ne m'étonne pas qu'un problème de config fasse tout merder.

Sinon c'est chouette de pouvoir garbage collecter (vive le français !!) quand tu veux mais ça a un effet pervers : le marquage est très lent.

Allez, avec des collisions à base bounding box (franchement, le pixel perfect dans un shoot ne sert à rien) tu devrais pouvoir faire du 500 ~ 600 boulettes/frame.
"HYPER GAGE : 500%"
Image
Avatar de l’utilisateur
niluge
Radiant Silverpost
Messages : 1247
Inscription : 29 juin 2006, 15:29
Localisation : Above and beyond

Et hop, une petite archive zip avec un jar et les fichiers image qui vont bien :
http://mac.faerley.free.fr/codingnotes/ ... upTest.zip

avec trois pattern différents (pas très inspiré mais bon)
c'est dur fait vite fait pour voir ce que ça donne, si la gestion des collisions fonctionne bien etc.

Si vous pouviez me confirmer que ça rame (ou non) sur Windows, linux, mac ...

pour aller plus loin, il me faudrait rajouter la gestions des animations (je verrais ça quand j'aurais le temps) de vraie graphisme et un level design qui vaut le coup ^^
Avatar de l’utilisateur
Radigo
Counter Stop
Messages : 7567
Inscription : 22 mai 2003, 17:31
Localisation : Paris
Contact :

Ça ne rame pas du tout sur mon ordi du boulot, mais il patate un peu trop à mon goût.

Et je confirme : les collisions pixel perfect dans un shmup c'est mal.
"HYPER GAGE : 500%"
Image
Avatar de l’utilisateur
niluge
Radiant Silverpost
Messages : 1247
Inscription : 29 juin 2006, 15:29
Localisation : Above and beyond

C'est marrant, ça depend énormément des configuration en fait (plus un petit problème de granularité du timer sous windows que je viens de corriger) et ce n'est pas forcement le meilleur proc que s'en sort le mieux

Sur mon plus vieux PC portable sous windows (P4 HT 2Ghz avec une carte graphique) il me faut de 0 à 1 ms pour gérer une frame

sur mon eee pc (atom n270 1,6Ghz) il faut entre 24 et 30 ms pour gérer une frame

et sur mon macbook unibody blanc (Dual core 2.2Ghz) il faut 5-6 ms pour gérer une frame

Allez, je vais maintenant m'amuser une peu avec ça. Faire un vrai et beau vaisseau animé pour commencer, et mettre en place deux trois ennemies pour aller un peu plus loin. Et il faut que je réfléchisse a un vrai gameplay
Avatar de l’utilisateur
Radigo
Counter Stop
Messages : 7567
Inscription : 22 mai 2003, 17:31
Localisation : Paris
Contact :

Yeah !

Bon courage en tout cas.
"HYPER GAGE : 500%"
Image
Répondre