Flame flame_forge2d实现人物移动 以及碰撞

355 阅读2分钟

之前通过Flame 实现了人物的移动以及边界的判断。地址juejin.cn/post/743995…

但是之前的逻辑是通过坐标移动物体。并通过碰撞来判断的到达边界。这种方式判断边界够用,但是如果是检测跟场景里面的障碍物碰撞,就不够用了。

下面的效果是使用flame_forge2d的api实现的,实现了边界碰撞以及场景内建筑物碰撞停止前进的效果。

output.webp

关键代码:

在flame_forge2d中,要设置二维属性,实现的是BodyComponent

image.png

然后重写createBody(设置二维属性)和onLoad(加载资源)

第一步:创建人物Body

@override
Body createBody() {
  final shape = PolygonShape()..setAsBoxXY(100, 100);
  final fixtureDef = FixtureDef(
    shape,
    restitution: 0,//设置回弹系数为0 碰撞后就不会反弹
    density: 1,
    friction: 0, //摩擦
  );
  final bodyDef = BodyDef(
      userData: this,
      angularDamping: 0.8,
      type: BodyType.dynamic,
      position: Vector2.zero()
  );
  renderBody = false;
  return world.createBody(bodyDef)..createFixture(fixtureDef)
    ..setFixedRotation(true);//设置碰撞后不旋转
}

第二步:加载人物内容

@override
Future<void> onLoad() async{
  add(PlayerFooterAura()
    ..anchor = Anchor.center
    ..size = Vector2(200, 200));

  gamePlayer = GamePlayer();
  add(gamePlayer);

  return super.onLoad();

}

第三步:创建建筑物就是那个小房子

image.png

重写onload 跟 createBody

@override
Future<void> onLoad() async{
  add(GameHouse());
  return super.onLoad();
}

@override
Body createBody() {
  final shape = PolygonShape()..setAsBoxXY(150, 100);
  final fixtureDef = FixtureDef(
    shape,
    restitution: 0,
    density: 0.1,
    friction: 0.0, //摩擦
  );
  renderBody = false;
  final bodyDef = BodyDef(
      userData: this,
      angularDamping: 0,
      type: BodyType.static,
      position: game.size / 4
  );
  return world.createBody(bodyDef)..createFixture(fixtureDef);
}

第四步:控制移动,这里判断键盘按键的方式还是跟之前的一样。不一样的是这里不通过坐标移动,而是通过给Body 设置速度来移动。

void onKeyEvent(KeyEvent event, Set<LogicalKeyboardKey> keysPressed){
  //判断键盘按下 上 下 左 右 方向
  final isKeyDown = event is KeyDownEvent;
  final keyLeft = (event.logicalKey == LogicalKeyboardKey.arrowLeft) ||
      (event.logicalKey == LogicalKeyboardKey.keyA);
  final keyRight = (event.logicalKey == LogicalKeyboardKey.arrowRight) ||
      (event.logicalKey == LogicalKeyboardKey.keyD);
  final keyUp = (event.logicalKey == LogicalKeyboardKey.arrowUp) ||
      (event.logicalKey == LogicalKeyboardKey.keyW);

  // var mass = 1 / body.inverseMass;
  // body.applyLinearImpulse(Vector2(mass * 300, mass * 300));

  debugPrint("=====KeyDownEvent===${event}");
  debugPrint("=====KeyDownEvent===${keysPressed.contains(LogicalKeyboardKey.keyA)}");
  if (isKeyDown) {
    if (keyLeft) {
      velocity.x = -runSpeed;
    } else if (keyRight) {
      velocity.x = runSpeed;
    }else if(keyUp){
      velocity.y = -runSpeed;
    }else {
      velocity.y = runSpeed;
    }
  } else {
    final hasLeft = keysPressed.contains(LogicalKeyboardKey.arrowLeft) ||
        keysPressed.contains(LogicalKeyboardKey.keyA);
    final hasRight = keysPressed.contains(LogicalKeyboardKey.arrowRight) ||
        keysPressed.contains(LogicalKeyboardKey.keyD);

    final hasTop = keysPressed.contains(LogicalKeyboardKey.arrowUp) ||
        keysPressed.contains(LogicalKeyboardKey.keyW);
    final hasBottom = keysPressed.contains(LogicalKeyboardKey.arrowDown) ||
        keysPressed.contains(LogicalKeyboardKey.keyS);
    if (hasLeft && hasRight) {
      velocity.x = 0;
    } else if (hasLeft) {
      velocity.x = -runSpeed;
    } else if (hasRight) {
      velocity.x = runSpeed;
    }else {
      velocity.x = 0;
    }

    if (hasTop && hasBottom) {
      velocity.y = 0;
    } else if (hasTop) {
      velocity.y = -runSpeed;
    } else if (hasBottom) {
      velocity.y = runSpeed;
    } else {
      velocity.y = 0;
    }
  }

  body.linearVelocity = velocity;

  if(velocity.y > 0){
    gamePlayer.updateAnim(3);
  }else if(velocity.y < 0){
    gamePlayer.updateAnim(2);
  }else if(velocity.x > 0){
    gamePlayer.updateAnim(1);
  }else if(velocity.x < 0){
    gamePlayer.updateAnim(0);
  }else {
    gamePlayer.updateAnim(3);
  }
}

关键点: body.linearVelocity = velocity;

第五步:设置边界

import 'package:flame/extensions.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/cupertino.dart';

//创建wall 墙
List<Wall> createBoundaries(Forge2DGame game, {double? strokeWidth}) {
  final visibleRect = game.camera.visibleWorldRect;
  final topLeft = visibleRect.topLeft.toVector2();
  final topRight = visibleRect.topRight.toVector2();
  final bottomRight = visibleRect.bottomRight.toVector2();
  final bottomLeft = visibleRect.bottomLeft.toVector2();

  return [
    Wall(topLeft, topRight, strokeWidth: strokeWidth),
    Wall(topRight, bottomRight, strokeWidth: strokeWidth),
    Wall(bottomLeft, bottomRight, strokeWidth: strokeWidth),
    Wall(topLeft, bottomLeft, strokeWidth: strokeWidth),
  ];
}

class Wall extends BodyComponent {
  final Vector2 start;
  final Vector2 end;
  final double strokeWidth;

  Wall(this.start, this.end, {double? strokeWidth})
      : strokeWidth = strokeWidth ?? 1;

  @override
  Body createBody() {

    final shape = EdgeShape()..set(start, end);
    final fixtureDef = FixtureDef(shape, friction: 0.3);
    //EdgeShape 不可以设置  type: BodyType.dynamic,
    final bodyDef = BodyDef(
      userData: this, // To be able to determine object in collision
      position: Vector2.zero(), //相当于设置 相对于word 的位置
    );
    debugMode = true;
    paint.strokeWidth = strokeWidth;

    return world.createBody(bodyDef)..createFixture(fixtureDef);
  }
}

边界就是添加了四个BodyType 为static的Body。这四个Body就是根据 start end 以及strokeWidth确定的一个四边形。也就是效果图里面的白边。

这样运行 就可以达到图片的效果。