Outils pour utilisateurs

Outils du site


cours2007:poo1:exemples:jeu

Un petit jeu (à finir)

Visible à cette adresse (maj)

Code HTML

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 
    <title>Jeu - étape objet</title>
    <link rel="stylesheet" href="jeu.css" type="text/css" media="screen" title="no title" charset="utf-8" />
 
    <script src="../javascript/mootools-beta-1.2b2.js" type="text/javascript" charset="utf-8"></script>
 
    <script src="jeu.js" type="text/javascript" charset="utf-8"></script>
 
</head>
 
<body>
    <h2 id="titre">Chargement : <span id="nbrImages">...</span></h2>
    <div id="jeu_avion">
 
    </div>
 
</body>
</html>

Code CSS

#jeu_avion {
   width:400px;
   height:400px;
   position:relative;
   overflow:hidden;
}

Code JavaScript

Element.Properties.sprites = {
 
  set: function(sprites){
    this.store('sprites',sprites);
  },
 
  get: function(){
    return this.retrieve('sprites', {});
  },
 
  erase: function(){
    this.erase('sprite');
    this.eliminate('sprites');
  }
 
};
//TODO regrouper sprite et sprites en sprite.current et sprite.positions
Element.Properties.sprite = {
 
  set: function(sprite){
    if (sprite) {
      var sprites = this.retrieve('sprites',null);
      if (sprites) {
        if (sprites[sprite]) {
          this.setStyle('background-position',sprites[sprite].x+'px '+sprites[sprite].y+'px');
          this.store('sprite',sprite);
        }
      }
    }
  },
 
  get: function(){
    return this.retrieve('sprite', null);
  },
 
  erase: function(){
    this.eliminate('sprite');
    this.setStyle('background-position','0px 0px');
  }
 
};
 
 
var JeuAvion = new Class({
  Implements: Options,
  options: {
    tempo: 70,
    size: {
      width: 400,
      height: 400
    },
    baseElement: 'jeu_avion'
  },
  timer: null,
  actors: [],
  toRemoveActors: [],
  doneDomready: false,
  initialize: function(options){
    this.setOptions(options);
    var opt = this.options;
    opt.baseSize = $merge(opt.baseSize,opt.size);
    window.addEvent('domready',this.domready.bind(this));
  },
  mkActor: function(Klass,options){
    var actor = new Klass($merge(this.options,Klass.prototype.options,options));
    this.addActor(actor);
    return actor;
  },
  addActor: function(actor){
    this.actors.include(actor);
    actor.setRoot(this);
    if (this.doneDomready) {actor.domready();}
  },
  removeActor: function(actor){
    // mémorise l'acteur à sup (destroy fait pendant update fct pas avec IE)
    this.toRemoveActors.push(actor);
  },
  doRemoveActors: function(){
    var actors = this.actors;
    this.toRemoveActors.each(function(item){
        for (var i = actors.length; i--; i){
          if (actors[i] === item) {
            actors[i].doDestroy();
            actors.splice(i, 1);
          }
        }
      });
    this.toRemoveActors = [];
  },
  callActors: function(fctName){
    this.doRemoveActors();
    this.actors.each(function(actor){
      if (actor[fctName]) {actor[fctName]();}
      });
  },
  domready: function(){
    this.callActors('domready');
    this.timer = this.update.periodical(this.options.tempo,this);
    this.doneDomready = true;
  },
  update: function(){
    this.callActors('update');
  }
});
 
var Actor = new Class({
  Implements: Options,
  options: {
    directions: {
      x: 1,
      y: 1 
    },
    speed: {
      x: 0,
      y: 0
    },
    position: {
      x:0,
      y:0
    },
    element: {
      tagName: 'div',
      propreties: {
        styles: {
          position: 'absolute',
          top:  0,
          left: 0
        }
      }
    }
  },
  element: null,
  initialize: function(options){
    this.setOptions(options);
    var props = this.options.element.propreties;
    props.styles = $merge(options.size,props.styles);
    var bg = this.options.backgroundUrl;
    if ($defined(bg)) {
      this.setOptions({element:{propreties:{styles:{'background-image':'url('+bg+')'}}}});
    }
  },
  domready: function(){
    var opt = this.options;
    var elt = opt.element;
    this.element = new Element(elt.tagName,elt.propreties);
    this.element.inject(opt.baseElement);
  },
  update: function(){
    var pos = this.options.position;
    var spd = this.options.speed;
    var dir = this.options.directions;
    pos.x += dir.x * spd.x;
    pos.y += dir.y * spd.y;
  },
  setRoot: function(root){
    this.root = root;
  },
  destroy: function(){
    this.root.removeActor(this);
  },
  doDestroy: function(argument){
    this.element.destroy();
  }
});
 
var ActorBgScroll = new Class({
  Extends: Actor,
  options: {
    roll: {
      x: 10000,
      y: 10000
    }
  },
  update: function(){
    this.parent();
    var options = this.options;
    var pos = options.position;
    var roll = options.roll;
    pos.x = pos.x % roll.x;
    pos.y = pos.y % roll.y;
    this.element.setStyle('background-position','0px '+this.options.position.y+'px');
  }
});
 
var ActorBound = new Class({
  Extends: Actor,
  options: {
    limitMax: {},
    limitMin: {}
  },
  initialize: function(options){
    this.parent(options);
    var opt = this.options;
    opt.limitMax = $merge({
      x:(opt.baseSize.height-opt.size.height),
      y:(opt.baseSize.width -opt.size.width )
      },opt.limitMax);
    opt.limitMin = $merge({x:0,y:0},opt.limitMin);
  },
  update: function(){
    this.parent();
    var pos = this.options.position;
    var limitMax = this.options.limitMax;
    var limitMin = this.options.limitMin;
    pos.x = pos.x.limit(limitMin.x,limitMax.x);
    pos.y = pos.y.limit(limitMin.x,limitMax.y);
  }
});
 
var CtrlDirKb = new Class({
  Implements: Options,
  keys: {},
  options: {
    directions: {
      x: 0,
      y: 0 
    }
  },
  initialize: function(){
    document.addEvent('keydown',this.keyEvent.bindWithEvent(this,true));
    document.addEvent('keyup',this.keyEvent.bindWithEvent(this,false));
    //document.addEvent('keypress',function(){return false;});
  },
  keyEvent: function(event,status){
    var keys = this.keys;
    keys[event.key] = status;
    var dx = 0, dy = 0;
    if ($defined(keys.up   ) && keys.up   ) {dx -= 1;}
    if ($defined(keys.down ) && keys.down ) {dx += 1;}
    if ($defined(keys.left ) && keys.left ) {dy -= 1;}
    if ($defined(keys.right) && keys.right) {dy += 1;}
    this.setOptions({directions:{x:dx, y:dy}});
    this.ici = true;
    return !(['space','up','down','left','right'].include(event.key));
  }
});
 
 
var CtrlDirRamdon = new Class({
  Implements: Options,
  keys: {},
  options: {
    directions: {
      x: 0,
      y: 0 
    },
    biais: {
      x: 1.2,
      y: 1 
    },
    reflect:['x','y']
  },
  initialize: function(){
    this.update();
    var self=this;
    this.options.reflect.each(function(d){
      ['Min','Max'].each(function(m){
        self['limit'+m+d.toUpperCase()] = function(){self.limit(d);};
      });
    });
  },
  update: function(){
    var biais = this.options.biais;
    this.setOptions({directions:{x:(biais.x-2*Math.random()), y:(biais.y-2*Math.random())}});
    this.update.delay(Math.random()*3000,this);
  },
  limit: function(d){
    this.options.directions[d] *= -1;
  }
});
 
var ActorCtrl = new Class({
  Extends: ActorBound,
  setCtrl: function(ctrl){
    this.ctrl = ctrl;
    return this;
  },
  update: function(){
    var ctrl = this.ctrl;
    this.setOptions(ctrl.options);
    this.parent();
    var pos = this.options.position;
    var limitMin = this.options.limitMin;
    var limitMax = this.options.limitMax;
    if ((pos.x<=limitMin.x)&& ctrl.limitMinX) {ctrl.limitMinX(this);}
    if ((pos.y<=limitMin.y)&& ctrl.limitMinY) {ctrl.limitMinY(this);}
    if ((pos.x>=limitMax.x)&& ctrl.limitMaxX) {ctrl.limitMaxX(this);}
    if ((pos.y>=limitMax.y)&& ctrl.limitMaxY) {ctrl.limitMaxY(this);}
    //TODO vérifier l'ordre des m-a-j (element.setStyles vide ?)
    if (this.element.setStyles) {
      this.element.setStyles({
        top:  pos.x,
        left: pos.y
      });
    }
  }
});
 
//TODO utiliser un autre moyen pour composer les comportements
var ActorAnim = new Class({
  Extends: ActorCtrl,
  domready: function(){
    this.parent();
    this.updateSprites();
  },
  updateSprites: function(){
    this.nbrSprites = this.element.get('sprites').length;
    this.currentSprite =  0;
  },
  update: function(){
    this.currentSprite = (this.currentSprite + 1) % this.nbrSprites;
    this.element.set('sprite',this.currentSprite);
    this.parent();
  }
});
 
function $R(a,b) {
  var t = [];
  for (var i=a; i <= b; i++) {
    t.push(i);
  }
  return t;
}
 
var Tir = new Class({
  Extends: ActorAnim,
  options: {
    size:{
      width:  62,
      height: 40
    },
    backgroundUrl: '../images/tir.png',
    element:{propreties:{
      sprites:($R(1,5).map(function(item,index){return {x:0,y:(index*-40)};})),
      sprite:'0'
    }}
  },
  initialize: function(options){
    this.ctrl = {
      options:{
        speed:{
          x:5
        },
        directions:{
          x:-1
        }
      },
      limitMinX:(this.destroy.bind(this))
    };
    this.parent(options);
  }
});
 
var Avion = new Class({
  Extends: ActorCtrl,
  options:{
     size:{
        width:62,
        height:90
      },
      speed: {
        x: 2,
        y: 3
      }, 
      position:{
        x:300,
        y:170
      },
      backgroundUrl:'../images/avion_sprite.png',
      element:{propreties:{
          sprites:{
            normal: {x:-45,y:-45},
            gauche: {x:-45,y:-225},
            droite: {x:-45,y:-405},
            avance: {x:-45,y:-585},
            accel:  {x:-45,y:-765},
            arriere:{x:-45,y:-945}
          },
          sprite:'normal'
        }}
    },
  domready: function(){
    this.parent();
    //TODO généraliser composite
    var opt = this.options;
    var elt = opt.element;
    this.element_inc = new Element(elt.tagName,elt.propreties);
    this.element_inc.inject(this.element);
    this.munitions = 0;
  },
  update: function(){
    this.parent();
    var dirY = this.options.directions.y;
    var sprite = null;
    if (dirY > 0.1) {
      sprite = "droite";
    } else if (dirY < -0.1) {
      sprite = "gauche";
    } else {
      sprite = "normal";
    }
    this.element.set('sprite',sprite);
    //TODO regrouper est simplifier le lien sprites/directions
    var dirX = this.options.directions.x;
    var sprite_inc = null;
    if (dirX > 0.1) {
      sprite_inc = "arriere";
    } else if (dirX < -0.1) {
      sprite_inc = "accel";
    } else {
      sprite_inc = "avance";
    }
    this.element_inc.set('sprite',sprite_inc);
 
    // tir
    this.munitions++;
    if (this.ctrl.keys.space && (this.munitions > 0)) {
      this.root.mkActor(Tir,{position:this.options.position});
      this.munitions = -14;
    }
  }
});
 
var Soucoupe = new Class({
  Extends:ActorAnim,
  mkSoucoupeOptions: function() {
    return {
      size:{
        width:  50,
        height: 50
      },
      speed:{
        //TODO inverse x/y ?
        x: 3,
        y: 1
      },
      backgroundUrl: '../images/ovni.png',
      element:{propreties:{
        sprites:($R(1,24).map(function(item,index){return {x:(index*-50),y:0};})),
        sprite:'0'
      }},
      position:{
        x:(Math.random()*200),
        y:(Math.random()*400)
      }
    };
  },
  initialize: function(options){
    options = $merge(options,this.mkSoucoupeOptions());
    this.parent(options);
    this.ctrl = new CtrlDirRamdon();
  }
});
 
window.addEvent('domready', function(){
  if (Browser.Engine.trident4) {
    $('titre').set('html','Désoler, mais <a href="http://www.mozilla.org/">la solution la plus simple est ...</a>. Ou IE7 pour avoir les png avec alpha');
  } else {
    var imagesJeu = [
      '../images/fond.jpeg',
      '../images/nuages.png',
      '../images/ovni.png',
      '../images/avion_sprite.png',
      '../images/tir.png'];
 
    var nbrImages = imagesJeu.length;
    Asset.images(imagesJeu, {
        'onProgress':function(nbr){
          $('nbrImages').set('text',nbr+'/'+nbrImages);
        },
        'onComplete': function(){
          $('titre').setText("Utilisez les touches fléchées et la bar d'espace")
          initJeu();
        }
      });
  }
});
 
function initJeu() {
  var jeu = new JeuAvion();
  jeu.mkActor(ActorBgScroll,{
    speed:{y:1},
    roll:{y:1200},
    backgroundUrl:'../images/fond.jpeg'
    });
  jeu.mkActor(ActorBgScroll,{
    speed:{y:3},
    roll:{y:1200},
    backgroundUrl:'../images/nuages.png'
    });
  var kb = new CtrlDirKb();
  jeu.mkActor(Avion).setCtrl(kb);
 
  jeu.mkActor(Soucoupe);
  jeu.mkActor(Soucoupe);
  jeu.mkActor(Soucoupe);
  jeu.mkActor(Soucoupe);
}

nécessite mootools 1.2b2 avec correctif 1348

cours2007/poo1/exemples/jeu.txt · Dernière modification: 2008/04/24 22:00 (modification externe)