Как передвигать персонажа на двух скоростях, не затрагивая при этом одну?

ОК, это немного сложно объяснить. Я делаю основанную на холсте 2-мерную игру "укажи и нажми". Вы можете осмотреться (переместить окружение), перетаскивая мышь горизонтально по экрану. И переместите персонажа, нажав, куда вы хотите, чтобы он пошел. Вроде как Эта Моя Война. Вот упрощенная версия того, что я получил...

ДЕЙСТВИЯ МЫШИ:

var held, mouseX, mouseXInitial;
window.addEventListener('mousedown',function(e){
    held = true;
    mouseXInitial = mouseX; 
});
window.addEventListener('mouseup',function(e){
    held = false;
});
window.addEventListener('mousemove',function(e){
    mouseX = e.clientX;
});
mouseEvents();

СМОТРЕТЬ ВО ВРЕМЯ (перетаскивая по экрану, чтобы посмотреть вокруг):

var sharedParentObject = {
    scrolledAmount: null,
    scrolling: function(){
        if (held){
            this.scrolledAmount = mouseX - mouseXInitial;
            this.x = this.xInitial + this.scrolledAmount;
        }
    },
    inputShared: function(){
        var that = this;
        window.addEventListener('mousedown',function(e){
            that.xInitial = that.x;
        });
        window.addEventListener('mousemove',function(e){
            that.scrolling();
        });
    }
}

ДВИЖЕНИЕ ХАРАКТЕРА:

 function Character(){
    this.speed = 2;
    this.target = null;
    this.input = function(){
        var that = this;
        window.addEventListener('mouseup',function(e){
            that.target = that.mouseXInitial;
            that.target += that.scrolledAmount;
        });
    }
    this.update = function(){
        if (this.target){
            //moving right
            if (this.x + this.w/2 < this.target){
                this.x += this.speed;
            }
            //moving left
            if (this.x + this.w/2 > this.target){
                this.x -= this.speed;
            }
        }
    }
 }

 Character.prototype = Object.create(sharedParentObject);

Это работает, но проблема в том, что как только я начинаю перетаскивать экран, чтобы осмотреться, пока персонаж уже гуляет, он становится все странным и нервным. Я понимаю, почему это происходит. Символ x изменяется одновременно как в классе символов, так и в родительском классе. Есть ли способ получить его, чтобы персонаж мог идти к цели, продолжая двигаться, пока я прокручиваю окружение? Вроде делаю и то, и другое не влияя на другое..

1 ответ

Все, что вам нужно сделать, это отследить расстояние, на которое мышь переместилась между событиями onmousedown и onmouseup. Если расстояние очень мало, то пользователь нажал на одну точку, если расстояние больше, то они пытаются панорамировать сцену.

<!doctype html>
<html>
 <head>
  <meta charset="utf-8">
  <style>
   body {
    background-color: black;
   }
   
   canvas {
    display: block;
    margin-top: 20px;
    margin-left: auto;
    margin-right: auto;
    border: solid 1px white;
    border-radius: 10px;
   }
  </style>
 </head>
 <body>
  <canvas id="canvas"></canvas>
  <script type="application/javascript">
  
  // ES5 Friendly class functions
  // (Supports overloading & single inheritance)
  var _class = (function() {
  
   "use strict";
   
   /*
    All a class is in JS is a function that
    acts as a constructor and an object (prototype)
    that holds the properties shared across all
    instances (static vars, constants & functions).
   */
   
   function _class(constructor,prototype) {
    prototype.base = null;
    prototype.super = constructor;
    
    constructor.prototype = prototype;
    
    return constructor;
   }
   
   _class.extends = function(base,constructor,prototype) {
    for (var property in base.prototype) {
     if (!prototype[property]) {
      prototype[property] = base.prototype[property];
     }
    }
    
    function bundledConstructor() {
     base.apply(this,arguments);
     constructor.apply(this,arguments);
    }
    
    prototype.base = base;
    prototype.super = bundledConstructor;
    
    bundledConstructor.prototype = prototype;
    
    return bundledConstructor;
   }
   
   return _class;
  
  })();
  
  void function() {
   
   "use strict";
   
   // Classes
   
   // Encapsulate canvas element
   var Viewport = _class(
    function(canvasID,width,height,bgColour) {
     this.canvas = document.getElementById(canvasID) || null;
     this.width = width || 1.0;
     this.height = height || 1.0;
     this.bgColour = bgColour || "gray";
     this.hWidth = this.width >> 1;
     this.hHeight = this.height >> 1;
     this.canvas.width = this.width;
     this.canvas.height = this.height;
     this.ctx = this.canvas.getContext("2d");
     this.ctx.translate(this.hWidth,this.hHeight);
     
     var bounds = this.canvas.getBoundingClientRect();
     
     this.leftMargin = bounds.left;
     this.topMargin = bounds.top;
    },{
     clear: function() {
      var ctx = this.ctx;
      ctx.fillStyle = this.bgColour;
      ctx.fillRect(-this.hWidth,-this.hHeight,this.width,this.height);
     }
    }
   );
   
   // This is a class used to encapsulate
   // the scene's panning/zooming.
   var Camera = _class(
    // Constructor Function
    function(viewport,x,y) {
     this.viewport = viewport || null;
     this.x = x || 0.0;
     this.y = y || 0.0;
     this.scale = 1.0;
     this.invScale = 1.0 / this.scale;
     
     this.allowUserInput = false;
     this.mouseDown = false;
     this.mouseLastX = 0.0;
     this.mouseLastY = 0.0;
     
     viewport.canvas.addEventListener("wheel",this.onwheel.bind(this));
     viewport.canvas.addEventListener("mousedown",this.onmousedown.bind(this));
     viewport.canvas.addEventListener("mouseup",this.onmouseup.bind(this));
     viewport.canvas.addEventListener("mousemove",this.onmousemove.bind(this));
    },
    
    // Prototype (constant values & functions go here)
    { 
     scaleCoordinates: function(x,y) {
      return [
       (x - this.viewport.hWidth) * this.invScale + this.x,
       (y - this.viewport.hHeight) * this.invScale + this.y
      ];
     },
     
     scaleDimensions: function(width,height) {
      return [
       width * this.invScale,
       height * this.invScale
      ];
     },
     
     pan: function(deltaX,deltaY) {
      this.x += deltaX;
      this.y += deltaY;
     },
     
     zoom: function(deltaScale) {
      this.scale += deltaScale;
      this.scale = Math.max(0.0,this.scale);
      this.invScale = 1.0 / this.scale;
     },
     
     onwheel: function(e) {
      if (this.allowUserInput) {
       var deltaY = -Math.sign(e.deltaY) * (e.deltaY / e.deltaY) * 0.25;
       
       this.zoom(deltaY);
      }
     },
     
     onmousedown: function(e) {
      this.mouseDown = true;
      
      [
       this.mouseLastX,
       this.mouseLastY
      ] = this.scaleCoordinates(
       e.clientX - this.viewport.leftMargin,
       e.clientY - this.viewport.topMargin
      );
     },
     
     onmouseup: function(e) {
      this.mouseDown = false;
     },
     
     onmousemove: function(e) {
      if (this.allowUserInput && this.mouseDown) {
       var [
        mouseX,
        mouseY
       ] = this.scaleCoordinates(
        e.clientX - this.viewport.leftMargin,
        e.clientY - this.viewport.topMargin
       );
       
       this.pan(
        this.mouseLastX - mouseX,
        this.mouseLastY - mouseY
       );
      }
     }
    }
   );
   
   // Contains basic behaviour all game objects will have
   var GameObject = _class(
    function(x,y,width,height,colour) {
     this.x = x || 0.0;
     this.y = y || 0.0;
     this.width = width || 1.0;
     this.height = height || 1.0;
     this.colour = colour || "darkblue";
     this.dx = 0.0;
     this.dy = 0.0;     
     this._draw_x = 0.0;
     this._draw_y = 0.0;
     this._draw_width = 0.0;
     this._draw_height = 0.0;
    },{
     updateDrawParameters: function(camera) {
      this._draw_width = this.width * camera.scale;
      this._draw_height = this.height * camera.scale;
      this._draw_x = ((this.x - camera.x) * camera.scale) - this._draw_width * 0.5;
      this._draw_y = ((this.y - camera.y) * camera.scale) - this._draw_height * 0.5;
     },
     
     tick: function() {
      
     },
     
     render: function(viewport) {
      var ctx = viewport.ctx;
      ctx.fillStyle = this.colour;
      ctx.strokeStyle = "black";
      ctx.lineWidth = 2;
      ctx.beginPath();
      ctx.rect(this._draw_x,this._draw_y,this._draw_width,this._draw_height);
      ctx.fill();
      ctx.stroke();
     }
    }
   );
   
   // A more specialized type of game object that
   // can move to a specific location
   var MoveAgent = _class.extends(GameObject,
    function(x,y,width,height,colour) {
     this.currentState = this.STATE_IDLE;
     this.targetX = 0.0;
     this.targetY = 0.0;
    },{
     STATE_IDLE: 1000,
     STATE_MOVING_TO_TARGET: 1001,
     
     MOVE_SPEED: 1.0,
     GOAL_TOLERANCE: 5.0, // How close is 'good enough' to the target
     
     moveTo: function(x,y) {
      this.targetX = x || 0.0;
      this.targetY = y || 0.0;
      this.currentState = this.STATE_MOVING_TO_TARGET;
     },
     
     tick: function() {
      switch(this.currentState) {
       case this.STATE_IDLE:
        
       break;
       
       case this.STATE_MOVING_TO_TARGET:
        var x = this.targetX - this.x;
        var y = this.targetY - this.y;
        var l = x * x + y * y;
        
        if (l < this.GOAL_TOLERANCE * this.GOAL_TOLERANCE) {
         this.currentState = this.STATE_IDLE;
        } else {
         l = 1.0 / Math.sqrt(l);
         this.dx = x * l * this.MOVE_SPEED;
         this.dy = y * l * this.MOVE_SPEED;
         this.x += this.dx;
         this.y += this.dy;
        }
       break;
      }
     }
    }
   );
   
   var ControlledMoveAgent = _class.extends(MoveAgent,
    function(x,y,width,height,colour,camera) {
     this.camera = camera || null;
     
     this.mouseDown = false;
     this.mouseX = 0.0;
     this.mouseY = 0.0;
    
     viewport.canvas.addEventListener("mousedown",this.onmousedown.bind(this));
     viewport.canvas.addEventListener("mouseup",this.onmouseup.bind(this));
     viewport.canvas.addEventListener("mousemove",this.onmousemove.bind(this));
    },{
     MOVE_TOLLERANCE: 5.0,
    
     onmousedown: function(e) {
      if (e.button === 0) {
       this.mouseDown = true;
       this.mouseX = e.clientX;
       this.mouseY = e.clientY;
      }
     },
     
     onmouseup: function(e) {
      if (e.button === 0 && this.mouseDown) {
       this.mouseDown = false;
       
       var x = e.clientX - this.camera.viewport.leftMargin;
       var y = e.clientY - this.camera.viewport.topMargin;
       
       [x,y] = this.camera.scaleCoordinates(x,y);
       
       this.moveTo(x,y);
      }
     },
     
     onmousemove: function(e) {
      if (this.mouseDown) {
       var x = this.mouseX - e.clientX;
       var y = this.mouseY - e.clientY;
       var l = Math.sqrt(x * x + y * y);
       
       if (l > this.MOVE_TOLLERANCE) {
        this.mouseDown = false;
       }
      }
     }
    }
   );
   
   // Vars
   var camera = null;
   var viewport = null;
   var scenery = [];
   var character = null;
   
   // Functions
   function loop() {
    // Tick
    for (var i = 0; i < scenery.length; ++i) {
     scenery[i].updateDrawParameters(camera);
    }
    
    character.tick();
    character.updateDrawParameters(camera);
    
    // Render
    viewport.clear();
    
    for (var i = 0; i < scenery.length; ++i) {
     scenery[i].render(viewport);
    }
    
    character.render(viewport);
    
    //
    requestAnimationFrame(loop);
   }
   
   // Entry Point
   onload = function() {
    viewport = new Viewport("canvas",180,160,"gray");
    camera = new Camera(viewport,0,0);
    camera.allowUserInput = true;
    camera.zoom(0.25);
    
    for (var i = 0; i < 10; ++i) {
     scenery[i] = new GameObject(
      180 * Math.random() - 90,
      160 * Math.random() - 80,
      10 + Math.random() * 10,
      10 + Math.random() * 10,
      "#" + ((Math.random() * 255) | 0).toString(16)
       + ((Math.random() * 255) | 0).toString(16)
       + ((Math.random() * 255) | 0).toString(16)
     );
    }
    
    character = new ControlledMoveAgent(0,0,10,10,"darkred",camera);
    
    loop();
   }
  }()
  
  </script>
 </body>
</html>

Другие вопросы по тегам