如何限制圆在范围内的运动?

时间:2019-05-17 04:46:53

标签: javascript reactjs algorithm

我正在创建一个游戏,其中会有地图和一些svg圆(代表单位/部队),上面有一些计数。单位随鼠标的位置移动。还有一个圆圈代表范围。

我有两个需求

  • 单位移动时,请勿越过其范围
  • 如果鼠标超出范围,那么我希望单位在鼠标方向上在范围圆的圆周上。解释第二点的图像如下。

enter image description here

有两个问题

  • 如果我很快移动单位,它就会移出范围圈。
  • 如果单位移出圆圈,则不会再移动。

以下是代码段。我已经用评论尽可能地解释了。问题中最重要的部分是onMouseMove中的<Board />

//helper functions

//to get the difference b/w two array.(Absoulute difference)
const getAbsDist= (arr1,arr2) => arr1.map((x,i) => Math.abs(x - arr2[i]))
//to get diagonal distance coverered from vertical and horizontal distance covered
const getDiaDist = (x,y) => Math.sqrt((x*x) + (y*y)); 
const clone = (obj) => JSON.parse(JSON.stringify(obj)); //to clone object

class UnitBox extends React.Component {
  state = {};
  render() {
    const {
      x, //current vertical postion
      y, //current horizontal position
      count, 
      range, 
      moving, //determines whether a range circle will appear or not
      onMouseDown,
      index,
      //position from which we started moving unit
      _x, 
      _y
    } = this.props;
    return (
      <g className="ub">
        <g
          onMouseDown={() => onMouseDown(index)}
          transform={"translate(" + x + ',' + y + ')'}
        >
          <text id="one" selectable="false">{count}</text>
          <circle r="15" />

          <use xlinkHref="#one" />
        </g>
        {moving && (
          <circle r={range} cx={_x} cy={_y} className="range" />
        )}
      </g>
    );
  }
}

class Board extends React.Component {
  state = {
    movingUnit: -1,
    unitBoxes: [
      { moving: false, count: 10, x: 200, y: 100, range: 50 }
    ]
  };
  onMouseDownUnit = unitIndex => {
    const state = clone(this.state),
    unit = state.unitBoxes[unitIndex];
    state.movingUnit = unitIndex;
    unit.moving = true;
    //'_x' and '_y' are the cords from where we click mouse it repesents the initial position
    [unit._x, unit._y] = [unit.x, unit.y]; 
    this.setState(state);
  };
  onMouseUp = e => {
    const state = clone(this.state);
    const unit = state.unitBoxes[this.state.movingUnit];
    state.movingUnit = -1;
    if (unit) unit.moving = false;
    this.setState(state);
  };
  onMouseMove = e => {
    
    let { movingUnit, unitBoxes } = this.state;
    if (movingUnit === -1) return;
    const { clientX, clientY } = e;
    const unit = unitBoxes[movingUnit];
    const { x, y,_x, _y, range} = unit;
    //get the difference b/w initial positon and final position and last final position and get diagonal distance from that.
    let dist = getDiaDist(...getAbsDist([x, y], [_x, _y]));
    
    //if distance covered is less than range than move the object.
    if(dist < range){ 
      unit.x = parseInt(clientX);
      unit.y = parseInt(clientY);
    }
    this.setState({ movingUnit, unitBoxes });
  };
  render() {
    const { unitBoxes } = this.state;
    return (
      <svg
        height="1000px"
        width="1000px"
        onMouseMove={this.onMouseMove}
        onMouseUp={this.onMouseUp}
        style={{ backgroundColor: "orange" }}
      >
        {unitBoxes.map((x, i) => (
          <UnitBox
            onMouseDown={this.onMouseDownUnit}
            {...x}
            index={i}
            key={i}
          />
        ))}
      </svg>
    );
  }
}

ReactDOM.render(<Board/>,document.getElementById('root'))
.ub text {
    text-anchor: middle;
    alignment-baseline: central;
    font-family: sans-serif;
    font-weight: bold;
    font-size: 16px;
    fill: #454545;
}
.ub{
    cursor: pointer;
}
.ub circle {
    stroke: #707070;
    fill: #cacaca;
    stroke-width: 2px;
}
.ub .range{
    fill:green;
    fill-opacity: 0.3;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

1 个答案:

答案 0 :(得分:1)

您可以测量与实际位置dist的当前距离以及移动单元的最新更新位置,但是当鼠标超出有效范围时,该位置不会更新。改用鼠标指针的当前位置:

  let dist = getDiaDist(...getAbsDist([clientX, clientY], [_x, _y]));

此更改将使单位保持在其范围内,但是当鼠标移至范围之外时不会更新其位置。为此,请通过沿鼠标指针的方向将单元“夹紧”到范围来处理距离大于范围的情况:

  if (dist < range) { 
      unit.x = clientX;
      unit.y = clientY;
  } else {
      let xx = clientX;
      let yy = clientY;
      let cc = range / dist;

      unit.x = _x + (xx - _x) * cc;
      unit.y = _y + (yy - _y) * cc;
  }