プログラミング初心者がphina.jsでゲームを作る⑥

phina.js
在宅さん
在宅さん

前回はプレイヤーを瞬間移動で動かせるようになったけど、今回は歩いているような感じに移動させてみよう。あとはマップに障害物を配置しよう。

プレイヤーを移動させる

前回は移動時に直接プレイヤーのY軸の値を変化させて、瞬間移動で移動させました。
今回は、プレイヤーに速度を持たせて次のマスまで移動すると速度を0にして止まらせます。速度はphysical.velocity.xとphysical.velocity.yに値を設定します。
player_velocityは、自分の好みで設定しますが、私は7ぐらいがちょうどよい好みでした。
上下左右のフラグを用意して、フラグが’1’の場合は移動中として扱い移動処理を行わないようにしてます。

var  player_velocity=7;
・・・略・・・ 
    if(this.moveleftflagg==1||this.moverightflagg==1||this.moveupflagg==1||this.movedownflagg==1){
        this.moveflagg=1;
    }else{
        this.moveflagg=0;
    }
・・・略・・・
  if(this.moveupflagg==1){
      if(this.y>this.caly(index_y)){
        this.physical.velocity.y=-1*player_velocity;
      }
      else if(this.y<this.caly(index_y)){
        this.y=this.caly(index_y);
        this.physical.velocity.y=0;
        this.moveupflagg=0;
      }
    }
    if(this.movedownflagg==1){
      if(this.y<this.caly(index_y)){
        this.physical.velocity.y=player_velocity;
      }
      else if(this.y>this.caly(index_y)){
        this.y=this.caly(index_y);
        this.physical.velocity.y=0;
        this.movedownflagg=0;
      }
    }
    if(this.moveleftflagg==1){
      if(this.x>this.calx(index_x)){
        this.physical.velocity.x=-1*player_velocity;
      }
      else if(this.x<this.calx(index_x)){
        this.x=this.calx(index_x);
        this.physical.velocity.x=0;
        this.moveleftflagg=0;
      }
    }
    if(this.moverightflagg==1){
      if(this.x<this.calx(index_x)){
        this.physical.velocity.x=player_velocity;
      }
      else if(this.x>this.calx(index_x)){
        this.x=this.calx(index_x);
        this.physical.velocity.x=0;
        this.moverightflagg=0;
      }
    }

これで、プレイヤーが歩いているような動きになります。

障害物の配置

次に障害物の配置をします。まず障害物用のクラスを作成します。
障害物クラスはマス目を黒く塗りつぶしで対応します。いずれはブロックの絵とかを配置します。
マス目の長さ(length)は50ですが線の長さ(lineWidth)が2なので、塗りつぶす四角形長さはlength-lineWidthで計算します。マス目の位置はcalx,calyで計算できるようにしたので、プレイヤークラスと同じ計算式を使っています。

phina.define("block", {
   superClass: 'RectangleShape',
   init: function(index_x,index_y) {
     this.superInit();
     this.width=length-lineWidth;
     this.height=length-lineWidth;
     this.fill="black";
     this.strokeWidth=0;
     this.x=this.calx(index_x);
     this.y=this.caly(index_y);
  },
  calx:function(index_x){
    var x = (width_span+length/2)+length*(index_x);
    return x;
  },
  caly:function(index_y){
    var y = (height_span+length/2)+length*(index_y);
    return y;
  },
});

これで障害物クラスができました。次に実際に障害物を配置します。配置する前に、今回プレイヤーと障害物を配置するためにマス目のどこに何のクラスが存在するかを管理する配列を作ります。作り方は簡単です。

    var ban;
  ban = new Array(max_index);//盤のどこになにを配置するかの配列 現在 0:何もなし 1:黒ブロック  2:プレイヤー
    for (var x = 0; x < max_index; x++){
      ban[x] = new Array(max_index);
    }
    for (var p = 0; p < max_index; p++){
      for (var q = 0; q < max_index; q++){
        ban[p][q] =0;
      }
    }

この配列に障害物は”1″、プレイヤーは”2″を設定して盤面を管理します。
console.logで盤面を表示させるとこんな感じにどこに何が設定されているかがわかります。

console.log(ban);

さて下準備ができたので実際に配置します。今回はランダムに10個配置します。
ランダム関数で出た位置が0であった場合に配置します。これを繰り返し10個配置できるまでくり返します。

for(var count=0;count<10;){//初期ブロック配置列に1を設定
      var x = Random.randint(0,max_index-1);
      var y = Random.randint(0,max_index-1);
      if(ban[x][y]==0){
        ban[x][y]=1;
        block(x,y).addChildTo(this);
        count++;
      }
}

以下全コードとなります。

phina.globalize();
var PLAYER_SIZE_X     = 20;
var PLAYER_SIZE_Y    = 32;
var SPEED =10;
var SCREEN_HEIGHT;
var SCREEN_WIDTH;
var height_span = 50;
var width_span = 20;
var length = 50;
var max_index=12;
var ban;
var lineWidth=2;
var player_velocity=7;
phina.define("StageScene", {
  superClass: 'DisplayScene',
  init: function() {
    this.superInit({
      backgroundColor:'hsl(200, 80%, 64%)',//背景色設定
    });
    SCREEN_WIDTH=this.gridX.width;
    SCREEN_HEIGHT=this.gridY.width;
    field().addChildTo(this).setPosition(this.gridX.center(), this.gridY.center());//fieldを展開
    ban = new Array(max_index);//盤のどこになにを配置するかの配列 現在 0:何もなし 1:黒ブロック  2:プレイヤー
    for (var x = 0; x < max_index; x++){
      ban[x] = new Array(max_index);
    }
    for (var p = 0; p < max_index; p++){
      for (var q = 0; q < max_index; q++){
        ban[p][q] =0;
      }
    }
    player(5,max_index-1).addChildTo(this);//プレイヤーを5,5の位置に配置する
    ban[5][max_index-1]=2;

    for(var count=0;count<10;){//初期ブロック配置列に1を設定
      var x = Random.randint(0,max_index-1);
      var y = Random.randint(0,max_index-1);
      if(ban[x][y]==0){
        ban[x][y]=1;
        block(x,y).addChildTo(this);
        count++;
      }
    }
  },
});

phina.define("player", {//プレイヤークラス
  superClass: 'Sprite',
  init: function(index_x,index_y) {
    this.superInit('player', PLAYER_SIZE_X, PLAYER_SIZE_Y);
    var ani = FrameAnimation('player_ss');
    ani.attachTo(this).gotoAndPlay('walk');
    this.index_x = index_x;
    this.index_y = index_y;
    this.keyflagg=0;
    this.leftflagg=0;
    this.rightflagg=0;
    this.upflagg=0;
    this.downflagg=0;
    this.moveflagg=0;
    this.moveleftflagg=0;
    this.moverightflagg=0;
    this.moveupflagg=0;
    this.movedownflagg=0;
    this.x=this.calx(this.index_x);
    this.y=this.caly(this.index_y);
  },
  update:function(app){
    var key = app.keyboard;
    var self = this;
    if(this.moveleftflagg==1||this.moverightflagg==1||this.moveupflagg==1||this.movedownflagg==1){
          this.moveflagg=1;
    }else{
          this.moveflagg=0;
    }
    if(this.keyflagg==0){
      if (key.getKeyDown('left')&& key.getKeyAngle()==180 && this.moveflagg==0) {
        this.keyflagg=1;
        this.leftflagg=1;
        self.lef();
      }
      if (key.getKeyDown('right')&& key.getKeyAngle()==0 && this.moveflagg==0) {
        this.keyflagg=1;
        this.rightflagg=1;
        self.rig();
      }
      if (key.getKeyDown('up')&& key.getKeyAngle()==90&& this.moveflagg==0) {
        this.keyflagg=1;
        this.upflagg=1;
        self.up();
      }
      if (key.getKeyDown('down')&& key.getKeyAngle()==270 && this.moveflagg==0) {
        this.keyflagg=1;
        this.downflagg=1;
        self.down();
      }
    }
    if(this.keyflagg==1){
      if (key.getKeyUp('left')&&this.leftflagg==1) {
        this.keyflagg=0;
        this.leftflagg=0;
      }
      if (key.getKeyUp('right')&&this.rightflagg==1) {
        this.keyflagg=0;
        this.rightflagg=0;
       }
      if (key.getKeyUp('up')&&this.upflagg==1) {
        this.keyflagg=0;
        this.upflagg=0;
      }
      if (key.getKeyUp('down')&&this.downflagg==1) {
        this.keyflagg=0;
        this.downflagg=0;
      }
    }
    this.velox(this.index_x,this.index_y);
  },
  up:function(){
    if(this.index_y>0){
      if(ban[this.index_x][this.index_y-1]==0){
        ban[this.index_x][this.index_y]=0;
        this.index_y-=1;
        ban[this.index_x][this.index_y]=2;
        this.moveupflagg=1;
      }
    }
  },
  down:function(){
    if(this.index_y<(max_index-1)){
      if(ban[this.index_x][this.index_y+1]==0){
        ban[this.index_x][this.index_y]=0;
        this.index_y+=1;
        ban[this.index_x][this.index_y]=2;
        this.movedownflagg=1;
      }
    }
  },
  lef:function(){
    if(this.index_x>0){
      if(ban[this.index_x-1][this.index_y]==0){
        ban[this.index_x][this.index_y]=0;
        this.index_x-=1;
        ban[this.index_x][this.index_y]=2;
        this.moveleftflagg=1;
      }
    }
  },
  rig:function(){
    if(this.index_x<(max_index-1)){
      if(ban[this.index_x+1][this.index_y]==0){
        ban[this.index_x][this.index_y]=0;
        this.index_x+=1;
        ban[this.index_x][this.index_y]=2;
        this.moverightflagg=1;
      }
    }
  },
  calx:function(index_x){
    var x = (width_span+length/2)+length*(index_x);
    return x;
  },
  caly:function(index_y){
    var y = (height_span+length/2)+length*(index_y);
    return y;
  },
  velox:function(index_x,index_y){
    if(this.moveupflagg==1){
      if(this.y>this.caly(index_y)){
        this.physical.velocity.y=-1*player_velocity;
      }
      else if(this.y<this.caly(index_y)){
        this.y=this.caly(index_y);
        this.physical.velocity.y=0;
        this.moveupflagg=0;
      }
    }
    if(this.movedownflagg==1){
      if(this.y<this.caly(index_y)){
        this.physical.velocity.y=player_velocity;
      }
      else if(this.y>this.caly(index_y)){
        this.y=this.caly(index_y);
        this.physical.velocity.y=0;
        this.movedownflagg=0;
      }
    }
    if(this.moveleftflagg==1){
      if(this.x>this.calx(index_x)){
        this.physical.velocity.x=-1*player_velocity;
      }
      else if(this.x<this.calx(index_x)){
        this.x=this.calx(index_x);
        this.physical.velocity.x=0;
        this.moveleftflagg=0;
      }
    }
    if(this.moverightflagg==1){
      if(this.x<this.calx(index_x)){
        this.physical.velocity.x=player_velocity;
      }
      else if(this.x>this.calx(index_x)){
        this.x=this.calx(index_x);
        this.physical.velocity.x=0;
        this.moverightflagg=0;
      }
    }
  }
});

phina.define("field", {
  superClass: 'PlainElement',
  init: function() {
    this.superInit();
    this.canvas.setSize(SCREEN_WIDTH,SCREEN_HEIGHT);
    this.canvas.context.strokeStyle = 'black';
    this.canvas.context.lineWidth = lineWidth;
    for ( let i = 0; i < max_index+1; i++){
      this.canvas.drawLine(width_span+(length*i), height_span,width_span+(length*i),height_span+(length*max_index));
      this.canvas.drawLine(width_span,height_span+(length*i),SCREEN_WIDTH-width_span,length+(length*i));
    }
 },
});

 phina.define("block", {
   superClass: 'RectangleShape',
   init: function(index_x,index_y) {
     this.superInit();
     this.width=length-lineWidth;
     this.height=length-lineWidth;
     this.fill="black";
     this.strokeWidth=0;
     this.x=this.calx(index_x);
     this.y=this.caly(index_y);
  },
  calx:function(index_x){
    var x = (width_span+length/2)+length*(index_x);
    return x;
  },
  caly:function(index_y){
    var y = (height_span+length/2)+length*(index_y);
    return y;
  },
});
// phina.js をグローバル領域に展開
phina.globalize();
var ASSETS = {
  image: {
    'player': './sozai/player.png',
  },
  spritesheet: {
    "player_ss":{
      "frame": {//画像情報
        "width": 32,
        "height": 32,
        "cols": 3,
        "rows": 4,
      },
      "animations" : {
        "walk": {
          "frames": [0,1,2],
          "next": "walk",
          "frequency": 6,
        },
      }
    }
  },
};
phina.define("DefaultManager", {
  superClass: "ManagerScene",
  init: function() {
    this.superInit({
      scenes:[
          {
            className: 'TitleScene',
            label: 'title',
            nextLabel: 'main',
          },
          {
            className: 'MainScene',
            label: 'main',
            nextLabel: 'pause',
          },
      ],
    });
  },
});

phina.define("CountManager", {
  superClass: "ManagerScene",
  init: function() {
    this.superInit({
      startLabel:'Easy',
      scenes: [
        {
          className: 'StageScene',
          label: 'stage',
          nextLabel: 'pause',
        },
        {
          className: 'ResultScene',
          label: 'result',
        },
        {
          className: 'MainScene',
          label: 'main',
        },
      ]
    });
  },
  onfinish:function(){
  this.exit();
  }
});
phina.define('TitleScene', {
  superClass: 'DisplayScene',
  /**
  * @constructor
  */
 init: function(params) {
   this.superInit(params);

   params = ({}).$safe(params, TitleScene.defaults);
   this.backgroundColor = params.backgroundColor;

   this.fromJSON({
     children: {
       titleLabel: {
         className: 'phina.display.Label',
         arguments: {
           text: 'プログラミング初心者の勉強道',
           fill: params.fontColor,
           stroke: false,
           fontSize: 32,
         },
         x: this.gridX.center(),
         y: this.gridY.span(4),
       }
     }
   });

   if (params.exitType === 'touch') {
     this.fromJSON({
       children: {
         touchLabel: {
           className: 'phina.display.Label',
           arguments: {
             text: "TOUCH START",
             fill: params.fontColor,
             stroke: false,
             fontSize: 32,
           },
           x: this.gridX.center(),
           y: this.gridY.span(12),
         },
       },
     });


   }
 },
onpointend:function(){
  this.exit();
},
 _static: {
   defaults: {
     title: 'phina.js games',
     message: '',
     width: 640,
     height: 960,
     fontColor: 'black',
     backgroundColor: 'hsl(200, 80%, 64%)',
     backgroundImage: '',
     exitType: 'touch',
   },
 },
});
phina.define('MainScene', {
  superClass: 'DisplayScene',
  init: function() {
    this.superInit();
    this.backgroundColor = '#444';
    this.label = Label('どちらか押してね').addChildTo(this);
    this.label.x = this.gridX.center();
    this.label.y = this.gridY.center();
    this.label.fill = 'white';
    var main_scene=this;

    var button_count = Button({
          x: main_scene.gridX.center(-4),
          y: main_scene.gridY.span(12),
          width: 150,
          height: 100,
          text: "ゲーム起動",
          fontSize: 18,
          fontColor: 'black',
    }).addChildTo(this)
    .onpush=function(){
          main_scene.app.replaceScene(CountManager());
    };

    var button_finish = Button({
            x: main_scene.gridX.center(4),
            y: main_scene.gridY.span(12),
            width: 150,
            height: 100,
            text: "終わり",
            fontSize: 23,
            fontColor: 'black',
    }).addChildTo(this)
    .onpush=function(){
            main_scene.exit();
    };
  },
});
phina.main(function() {
  // アプリケーション生成
  var app = GameApp({
        assets: ASSETS,
        scene:DefaultManager(),
  });
  // アプリケーション実行
  app.run();
});

ranstantではtomopikoさんを動かしてます。

Forked: Forked: 2020/05/27 21:18:06 - Runstant | Runstant
思いたったらすぐ開発. プログラミングに革命を...
タイトルとURLをコピーしました