【问题标题】:how to Smooth catmull rom spline curve when it is moving like amaging wire in libgdx当catmull rom样条曲线像libgdx中的磁线一样移动时如何平滑它
【发布时间】:2017-06-03 07:34:08
【问题描述】:

我试图移动我的曲线但它移动得不好,当它从左到右或从右到左改变它的方向时,移动很尴尬。 我想像这个视频一样移动我的曲线 video of curve movement what i actually want。 在这个视频中,当它改变方向时,它是如此优雅,但在我的例子中,它改变了方向,曲线在新添加的点处呈现出疯狂的形状。 请高手解决这个问题。 这是我的代码

//create paths
private Bezier<Vector2> path1; 
private CatmullRomSpline<Vector2> path2;
private ShapeRenderer sr;
int height,width;
Vector2 starting,ending,endingControl;
ArrayList<Vector2> listOfPoints;
Vector3 touchPos;
float timeDifference;
Boolean leftPos=false,rightPos=false;
Boolean isTouch=false,isTouchUp=false;
Vector2 mVector2;
private OrthographicCamera cam;
Vector2[] controlPoints;

@Override
public void create () { 

     width = Gdx.graphics.getWidth();
     height = Gdx.graphics.getHeight();
     ending=new Vector2(width/2,height/2);
     endingControl=new Vector2(ending.x,ending.y+10);
     starting=new Vector2(width/2,0);

    controlPoints = new Vector2[]{starting,starting,ending,ending};



    // set up the curves

    path2 = new CatmullRomSpline<Vector2>(controlPoints, false);
    listOfPoints=new ArrayList<Vector2>();
    // setup ShapeRenderer
    sr = new ShapeRenderer();
    sr.setAutoShapeType(true);
    sr.setColor(Color.BLACK);
    cam=new OrthographicCamera();
    cam.setToOrtho(false);
    listOfPoints.add(new Vector2(width/2,0)); //starting 
    listOfPoints.add(new Vector2(width/2,0)); //starting

}

@Override
public void resize(int width, int height) {
    // TODO Auto-generated method stub
    super.resize(width, height);


    cam.update();
}

@Override
public void render () {
    cam.update();
    Gdx.gl.glClearColor(1f, 1f, 1f, 1f);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    sr.begin();
    sr.set(ShapeType.Filled);


    if(Gdx.input.isTouched())
    {


        if(!isTouch){
            listOfPoints.add(new Vector2(ending.x+2, ending.y-4));


            int s=listOfPoints.size();
            controlPoints=new Vector2[s+2];
            listOfPoints.toArray(controlPoints);


            controlPoints[s]=ending;
            //endingControl.x=ending.y;
            controlPoints[s+1]=ending;
            path2 = new CatmullRomSpline<Vector2>(controlPoints, false);

        }

        isTouch=true;
        ending.x+=3;





    }
    else {

        if(isTouch){
            listOfPoints.add(new Vector2(ending.x-2, ending.y-4));
           int s=listOfPoints.size();
            controlPoints=new Vector2[s+2];
            listOfPoints.toArray(controlPoints);


            controlPoints[s]=ending;

            controlPoints[s+1]=ending;

            path2 = new CatmullRomSpline<Vector2>(controlPoints, false);


        }
        isTouch=false;
        ending.x-=3;

    }


    moveAndReduce();


    for(int i = 0; i < 100; ++i){
        float t = i /100f;
        Vector2 st = new Vector2();
        Vector2 end = new Vector2();
        path2.valueAt(st,t);
        path2.valueAt(end, t-0.01f);
        sr.rectLine(st.x, st.y, end.x, end.y,3);

    }

    sr.end();

}

@Override
public void dispose () {
    sr.dispose();
}

public void moveAndReduce()
{
    for(Vector2 vector2:listOfPoints)
    {
        vector2.y-=3 ;

    }
    if(listOfPoints.size()>3 && listOfPoints.get(3).y<-1)
    {

        listOfPoints.remove(0);
        listOfPoints.set(0, listOfPoints.get(1));
        int s=listOfPoints.size();
        controlPoints=new Vector2[s+2];
        listOfPoints.toArray(controlPoints);
        controlPoints[s]=ending;
        controlPoints[s+1]=ending;    
        path2 = new CatmullRomSpline<Vector2>(controlPoints, false);    
    }
}

【问题讨论】:

    标签: java libgdx game-physics


    【解决方案1】:

    通过视频,曲线看起来不像是受控制点约束,而只是一个简单的轨迹和加速点。

    您创建一个浮点数组,其长度(以像素为单位)与 x 方向上的直线长度相匹配。例如,如果屏幕是 200 像素宽,则行可以是 100,因此数组的长度是 100。将数组中的每个浮点数设置为屏幕高度一半的起始值。我在这个答案中调用数组line。随你喜欢。

    您分配一个head 索引,它是最右边点的索引。将head 索引向上移动一帧。如果它在数组length-1 上,则将其设置为零(数组的开头)

    渲染路径

    当您绘制line 时,您会从head + 1 中绘制所有点

    Path p = new Path();
    for(int i = 0; i < 100; ++i){
       p.lineTo(i, line[(i + head + 1) % 100]); // add path points
    }
    // draw the path;
    

    上下移动

    要让它移动,你有一个移动浮点 move,它是 0 表示没有移动,或者正负值向上或向下移动。

    当您希望它移动时,将move 的数量增加一个固定值。

    // moving down
    if(move < maxMove){  // set a max move amount eg 10
        move += moveAmount;  // moveAmount 0.2 just as an example
    }
    

    向上移动也一样,但要减去

    当没有输入时,您将 move 数量以固定速率移回零

    // assume this is code run when no input
    if(move != 0){
        if(Math.abs(move) < moveAmount){ // if close to zero set to zero
           move = 0;
        }else{
           move -= Math.sign(move) * moveAmount; // else move towards zero at
                                                 // fixed rate
        }
    }
    

    前进

    这条线没有向前移动,只是在我们每帧将头部位置向上移动到数组中时似乎这样做了。

    回到移动线头以下移动线头位置向上或向下(但不完整最后一条线被修改以创建更平滑的曲线)

    float pos = line[head]; // get the pos of line at head
    head += 1; // move the head forward 1
    head %= 100; // if past end of array move to 0
    line[head] = pos + move; // set the new head position
    

    更好的曲线

    这将根据移动向上或向下移动线的头部。我们得到的曲线不是很好,所以为了让它更平滑一点,你需要改变移动值改变头部位置的速率。

    // an sCurve for any value of move the result is  from -1 to 1
    // the greater or smaller move the closer to 1 or -1 the value gets
    // the value -1.2 controls the rate at which the value moves to 1 or -1
    // the closer to -1 the value is the slower the value moves to 1 or -1    
    float res =  (2 / (1 + Math.pow(move,-1.2))) -1;
    

    这实际上在上下移动时将线条曲线的形状更改为几乎正弦波

    // so instead of
    //line[head] = pos + move; // set the new head position
    line[head] = pos + ( (2 / (1 + Math.pow(move,-1.2))) -1 ) * maxSpeed;
    // max speed is the max speed the line head can move up or down
    // per frame in pixels.
    

    曲线示例

    下面是一个如上所述的 Javascript 实现(不是作为答案代码)。使用键盘向上箭头和向下箭头移动线条

    如果您使用的是平板电脑或手机,那么下图是我为示例添加和测试触摸的迟到的方式

    const doFor = (count, callback) => {var i = 0; while (i < count) { callback(i ++) } };
    const keys = {
        ArrowUp : false,
        ArrowDown : false,
    };
    function keyEvents(e){
        if(keys[e.code] !== undefined){
            keys[e.code] = event.type === "keydown";
            e.preventDefault();
        }
    }
    addEventListener("keyup", keyEvents);
    addEventListener("keydown", keyEvents);
    focus();
    var gameOver = 0;
    var gameOverWait = 100;
    var score = 0;
    var nextWallIn = 500
    var nextWallCount = nextWallIn;
    var wallHole = 50;
    const wallWidth = 5;
    const walls = [];
    function addWall(){
      var y = (Math.random() * (H - wallHole  * 2)) + wallHole *0.5;
      walls.push({
          x : W,
          top : y,
          bottom : y + wallHole,
          point : 1, // score point
      });
    }
    function updateWalls(){
       nextWallCount += 1;
       if(nextWallCount >= nextWallIn){
          addWall();
          nextWallCount = 0;
          nextWallIn -= 1;
          wallHole -= 1;
       }
       for(var i = 0; i < walls.length; i ++){
          var w = walls[i];
          w.x -= 1;
          if(w.x < -wallWidth){
            walls.splice(i--,1);
          }
          if(w.x >= line.length- + wallWidth && w.x < line.length){
              var pos = line[head];
              if(pos < w.top || pos > w.bottom){
                  gameOver = gameOverWait;
              }
          }
          if(w.point > 0 && w.x <= line.length){
            score += w.point;
            w.point = 0;
          }
       }
    }
    function drawWalls(){
      for(var i = 0; i < walls.length; i ++){
          var w = walls[i];
          ctx.fillStyle = "red";
          ctx.fillRect(w.x,0,wallWidth,w.top);
          ctx.fillRect(w.x,w.bottom,wallWidth,H-w.bottom);
      }
    }
    const sCurve = (x,p) =>  (2 / (1 + Math.pow(p,-x))) -1;
    const ctx = canvas.getContext("2d");
    var W,H; // canvas width and height
    const line = [];
    var move = 0;
    var curvePower = 1.2;
    var curveSpeed = 0.2;
    var maxSpeed = 10;
    var headMoveMultiply = 2;
    var head;
    function init(){
      line.length = 0;
      doFor(W / 2,i => line[i] = H / 2);
      head = line.length - 1;
      move = 0;
      walls.length = 0;
      score = 0;
      nextWallIn = 500
      nextWallCount = nextWallIn;
      wallHole = 50;
      ctx.font = "30px arial black";
    }
    function stepLine(){
      var pos = line[head];
      head += 1;
      head %= line.length;
      
      line[head] = pos + sCurve(move,curvePower)*headMoveMultiply ;
    
    }
    function drawLine(){
      ctx.beginPath();
      ctx.strokeStyle = "black";
      ctx.lineWidth = 3;
      ctx.lineJoin = "round";
      ctx.lineCap = "round";
      for(var i = 0; i <line.length; i++){
        ctx.lineTo(i,line[(i + head + 1) % line.length]);
      }
      ctx.stroke();
    }
    
    function mainLoop(time){
        if(canvas.width !== innerWidth || canvas.height !== innerHeight){ 
            W = canvas.width = innerWidth;
            H = canvas.height = innerHeight;
            init();
        }
        if(gameOver === 1){
          gameOver = 0;
          init();
        }
        ctx.setTransform(1,0,0,1,0,0); 
        ctx.clearRect(0,0,W,H); 
        if(keys.ArrowUp){
            if(move > - maxSpeed){
              move -= curveSpeed;
            }
            
        }else if(keys.ArrowDown){
            if(move < maxSpeed){
               move += curveSpeed;
            }
        }else{
            move -= Math.sign(move)*curveSpeed;
            if(Math.abs(move) < curveSpeed){
                move = 0;
            }
        }
        if(gameOver === 0){
            stepLine();
            updateWalls();
        }
        drawLine();
        drawWalls();
        ctx.fillStyle = "Black";
        ctx.textAlign = "left";
        ctx.fillText("Score : " + score, 10,30);
        if(gameOver > 0){
            ctx.textAlign = "center";
            ctx.fillText("Crashed !!", W / 2,H * 0.4);
            gameOver -= 1;
        }
        requestAnimationFrame(mainLoop);
    }
    requestAnimationFrame(mainLoop);
    canvas {
        position : absolute;
        top : 0px;
        left : 0px;
        z-index : -10;
    }
    <br><br><br>Up down arrow keys to move line.
    <canvas id=canvas></canvas>

    【讨论】:

    • 先生,2d 中的所有内容都需要 x 和 y 值,但是在您的线阵列中,我无法理解如何存储 x 和 y 值,先生,您能解释一下吗?而且我不知道java脚本,所以如果你用java解释它,那么我会更容易理解一切先生。 @盲人67
    • @AvijitBiswas 您需要的所有代码都在 Java 的答案中,javascript 只是为了显示曲线,因为它可能不是您想要的。该数组仅存储 y 坐标,因为 x 坐标来自 for 循环。我将在哪里查看答案添加标题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-17
    相关资源
    最近更新 更多