constraint.dart 6.11 KB
Newer Older
1 2 3 4
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
part of flutter_sprites;
6

7 8 9 10 11
/// A constraint limits or otherwise controls a [Node]'s properties, such as
/// position or rotation. Add a list of constraints by setting the [Node]'s
/// constraints property.
///
/// Constrains are applied after the update calls are
12 13
/// completed. They can also be applied at any time by calling
/// [Node.applyConstraints]. It's possible to create custom constraints by
14
/// overriding this class and implementing the [constrain] method.
15
abstract class Constraint {
16 17 18
  /// Called before the node's update method is called. This method can be
  /// overridden to create setup work that needs to happen before the the
  /// node is updated, e.g. to calculate the node's speed.
19 20 21
  void preUpdate(Node node, double dt) {
  }

22 23 24
  /// Called after update is complete, if the constraint has been added to a
  /// [Node]. Override this method to modify the node's property according to
  /// the constraint.
25 26 27 28
  void constrain(Node node, double dt);
}

double _dampenRotation(double src, double dst, double dampening) {
Viktor Lidholt's avatar
Viktor Lidholt committed
29 30 31
  if (dampening == null)
    return dst;

32 33 34 35 36 37 38 39
  double delta = dst - src;
  while (delta > 180.0) delta -= 360;
  while (delta < -180) delta += 360;
  delta *= dampening;

  return src + delta;
}

40
/// A [Constraint] that aligns a nodes rotation to its movement.
41
class ConstraintRotationToMovement extends Constraint {
42 43
  /// Creates a new constraint the aligns a nodes rotation to its movement
  /// vector. A [baseRotation] and [dampening] can optionally be set.
44
  ConstraintRotationToMovement({this.baseRotation: 0.0, this.dampening});
45

46 47
  /// The filter factor used when constraining the rotation of the node. Valid
  /// values are in the range 0.0 to 1.0
48
  final double dampening;
49 50

  /// The base rotation will be added to a the movement vectors rotation.
51
  final double baseRotation;
52 53 54

  Point _lastPosition;

55
  @override
56 57 58 59
  void preUpdate(Node node, double dt) {
    _lastPosition = node.position;
  }

60
  @override
61
  void constrain(Node node, double dt) {
62
    if (_lastPosition == null) return;
63 64 65 66
    if (_lastPosition == node.position) return;

    // Get the target angle
    Offset offset = node.position - _lastPosition;
67
    double target = degrees(GameMath.atan2(offset.dy, offset.dx)) + baseRotation;
68

Viktor Lidholt's avatar
Viktor Lidholt committed
69
    node.rotation = _dampenRotation(node.rotation, target, dampening);
70 71
  }
}
72

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
/// A [Constraint] that copies a node's rotation, optionally with [dampening].
class ConstraintRotationToNodeRotation extends Constraint {
  /// Creates a new constraint that copies a node's rotation, optionally
  /// with a [baseRotation] added and using [dampening].
  ConstraintRotationToNodeRotation(this.targetNode, { this.baseRotation: 0.0, this.dampening });

  /// The node to copy the rotation from
  final Node targetNode;

  /// The base rotation will be added to the rotation that copied from the targetNode
  final double baseRotation;

  /// The filter factor used when constraining the rotation of the node. Valid
  /// values are in the range 0.0 to 1.0
  final double dampening;

  @override
  void constrain(Node node, double dt) {
    double target = targetNode.rotation + baseRotation;
    node.rotation = _dampenRotation(node.rotation, target, dampening);
  }
}

96 97 98
/// A [Constraint] that rotates a node to point towards another node. The target
/// node is allowed to have a different parent, but they must be in the same
/// [SpriteBox].
99
class ConstraintRotationToNode extends Constraint {
100 101 102
  /// Creates a new [Constraint] that rotates the node towards the [targetNode].
  /// The [baseRotation] will be added to the nodes rotation, and [dampening]
  /// can be used to ease the rotation.
103
  ConstraintRotationToNode(this.targetNode, {this.baseRotation: 0.0, this.dampening});
104

105
  /// The node to rotate towards.
106
  final Node targetNode;
107 108

  /// The base rotation will be added after the target rotation is calculated.
109
  final double baseRotation;
110 111 112

  /// The filter factor used when constraining the rotation of the node. Valid
  /// values are in the range 0.0 to 1.0
113 114
  final double dampening;

115
  @override
116 117 118
  void constrain(Node node, double dt) {
    Offset offset;

119 120 121 122 123
    if (targetNode.spriteBox != node.spriteBox) {
      // The target node is in another sprite box or has been removed
      return;
    }

124 125 126 127 128 129 130 131 132
    if (targetNode.parent == node.parent) {
      offset = targetNode.position - node.position;
    } else {
      offset = node.convertPointToBoxSpace(Point.origin)
        - targetNode.convertPointToBoxSpace(Point.origin);
    }

    double target = degrees(GameMath.atan2(offset.dy, offset.dx)) + baseRotation;

Viktor Lidholt's avatar
Viktor Lidholt committed
133
    node.rotation = _dampenRotation(node.rotation, target, dampening);
134 135
  }
}
136

137 138
/// A [Constraint] that constrains the position of a node to equal the position
/// of another node, optionally with dampening.
139
class ConstraintPositionToNode extends Constraint {
140 141 142 143
  /// Creates a new [Constraint] that constrains the poistion of a node to be
  /// equal to the position of the [targetNode]. Optionally an [offset] can
  /// be used and also [dampening]. The targetNode doesn't need to have the
  /// same parent, but they need to be added to the same [SpriteBox].
144 145
  ConstraintPositionToNode(this.targetNode, {this.dampening, this.offset: Offset.zero});

146
  /// Target node to follow.
147
  final Node targetNode;
148 149

  /// Offset to the target node.
150
  final Offset offset;
151 152

  /// Dampening used when following the [targetNode], value between 0.0 and 1.0.
153 154
  final double dampening;

155
  @override
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
  void constrain(Node node, double dt) {
    Point targetPosition;

    if (targetNode.spriteBox != node.spriteBox || node.parent == null) {
      // The target node is in another sprite box or has been removed
      return;
    }

    if (targetNode.parent == node.parent) {
      targetPosition = targetNode.position;
    } else {
      targetPosition = node.parent.convertPointFromNode(Point.origin, targetNode);
    }

    if (offset != null)
      targetPosition += offset;

    if (dampening == null)
      node.position = targetPosition;
    else
      node.position = GameMath.filterPoint(node.position, targetPosition, dampening);
  }
}