private void UpdateFromGamepad(Transform referenceObj, GamepadInput gamepad) { float referenceAngle = (referenceObj != null) ? referenceObj.Angle : 0.0f; if (gamepad.LeftThumbstick.Length > 0.25f) { float mappedLength = (gamepad.LeftThumbstick.Length - 0.25f) / 0.75f; this.controlMovement = gamepad.LeftThumbstick * mappedLength / gamepad.LeftThumbstick.Length; } else { this.controlMovement = Vector2.Zero; } if (gamepad.RightThumbstick.Length > 0.5f) { this.controlLookAngle = gamepad.RightThumbstick.Angle; this.controlLookSpeed = (gamepad.RightThumbstick.Length - 0.5f) / 0.5f; } else if (gamepad.LeftThumbstick.Length > 0.25f) { this.controlLookAngle = gamepad.LeftThumbstick.Angle; this.controlLookSpeed = (gamepad.LeftThumbstick.Length - 0.25f) / 0.75f; } bool targetAimed = MathF.CircularDist(referenceAngle, this.controlLookAngle) < MathF.RadAngle1 * 10; this.controlFireWeapon = (targetAimed && gamepad.RightThumbstick.Length > 0.9f) || gamepad[GamepadAxis.RightTrigger] > 0.5f || gamepad[GamepadButton.RightShoulder] || gamepad[GamepadButton.A]; this.controlQuit = gamepad.ButtonHit(GamepadButton.Back); this.controlStart = gamepad.ButtonHit(GamepadButton.Start); }
[Test] public void Perpendicular() { AssertVectorEqual(new Vector2(0.0f, -1.0f).PerpendicularLeft, -1.0f, 0.0f); AssertVectorEqual(new Vector2(1.0f, 0.0f).PerpendicularLeft, 0.0f, -1.0f); AssertVectorEqual(new Vector2(0.0f, 1.0f).PerpendicularLeft, 1.0f, 0.0f); AssertVectorEqual(new Vector2(-1.0f, 0.0f).PerpendicularLeft, 0.0f, 1.0f); AssertVectorEqual(new Vector2(0.0f, -1.0f).PerpendicularRight, 1.0f, 0.0f); AssertVectorEqual(new Vector2(1.0f, 0.0f).PerpendicularRight, 0.0f, 1.0f); AssertVectorEqual(new Vector2(0.0f, 1.0f).PerpendicularRight, -1.0f, 0.0f); AssertVectorEqual(new Vector2(-1.0f, 0.0f).PerpendicularRight, 0.0f, -1.0f); Random rnd = new Random(1); for (int i = 0; i < 100; i++) { Vector2 vector = rnd.NextVector2(); Vector2 left = vector.PerpendicularLeft; Vector2 right = vector.PerpendicularRight; Assert.AreEqual(MathF.RadAngle90, MathF.CircularDist(vector.Angle, left.Angle), Epsilon); Assert.AreEqual(MathF.RadAngle90, MathF.CircularDist(vector.Angle, right.Angle), Epsilon); Assert.AreEqual(-1.0f, MathF.TurnDir(vector.Angle, left.Angle)); Assert.AreEqual(1.0f, MathF.TurnDir(vector.Angle, right.Angle)); } }
void ICmpUpdatable.OnUpdate() { Transform transform = this.GameObj.Transform; CharacterController controller = this.GameObj.GetComponent <CharacterController>(); ActorRenderer sprite = this.GameObj.GetComponent <ActorRenderer>(); float targetMoveAngle = controller.TargetLookDir; bool isMoving = controller.TargetMovement.Length > 0.01f; if (isMoving) { sprite.AnimDuration = this.spriteSheetBaseDuration / controller.TargetMovement.Length; } sprite.AnimPaused = !isMoving; if (MathF.CircularDist(targetMoveAngle, 0.0f) <= MathF.RadAngle45) { sprite.AnimFirstFrame = this.spriteSheetIndex + this.spriteSheetOffset * 0; } else if (MathF.CircularDist(targetMoveAngle, MathF.RadAngle90) <= MathF.RadAngle45) { sprite.AnimFirstFrame = this.spriteSheetIndex + this.spriteSheetOffset * 1; } else if (MathF.CircularDist(targetMoveAngle, MathF.RadAngle270) <= MathF.RadAngle45) { sprite.AnimFirstFrame = this.spriteSheetIndex + this.spriteSheetOffset * 3; } else { sprite.AnimFirstFrame = this.spriteSheetIndex + this.spriteSheetOffset * 2; } }
public override void OnUpdate() { base.OnUpdate(); ActorRenderer sprite = this.GameObj.GetComponent <ActorRenderer>(); Transform transform = this.GameObj.Transform; Vector2 moveVec = transform.Vel.Xy; float moveDir = moveVec.Angle; if (moveVec.Length <= 0.75f) { sprite.AnimFirstFrame = 0; } else if (MathF.CircularDist(moveDir, 0.0f) <= MathF.RadAngle45) { sprite.AnimFirstFrame = 9; } else if (MathF.CircularDist(moveDir, MathF.RadAngle90) <= MathF.RadAngle45) { sprite.AnimFirstFrame = 6; } else if (MathF.CircularDist(moveDir, MathF.RadAngle270) <= MathF.RadAngle45) { sprite.AnimFirstFrame = 3; } else { sprite.AnimFirstFrame = 0; } this.lifeTimeMs += Time.TimeMult * Time.MsPFMult; if (this.lifeTimeMs > 500.0f) { this.Energy -= (Time.MsPFMult / (50.0f)) * Time.TimeMult; if (this.Energy + this.TransferEnergy <= 0.0f) { this.GameObj.Dispose(); } this.HurtCharacters(); } if (this.fireLoopInstance != null && this.fireLoopInstance.Disposed) { this.fireLoopInstance = null; } if (this.fireLoopInstance == null) { float scale = this.GetScale(this.Energy); if (scale > 0.05f && this.fireLoopSound != null) { this.fireLoopInstance = DualityApp.Sound.PlaySound3D(this.fireLoopSound, this.GameObj); this.fireLoopInstance.Looped = true; this.fireLoopInstance.Volume = scale; } } }
private void DrawLocalAngleConstraint(Canvas canvas, RigidBody body, Vector2 anchor, float targetAngle, float currentAngle, float radius) { Vector3 bodyPos = body.GameObj.Transform.Pos; ColorRgba clr = this.JointColor; ColorRgba clrErr = this.JointErrorColor; Vector2 anchorToWorld = body.GameObj.Transform.GetWorldVector(anchor); Vector2 angleVec = Vector2.FromAngleLength(targetAngle, radius); Vector2 errorVec = Vector2.FromAngleLength(currentAngle, radius); bool hasError = MathF.CircularDist(targetAngle, currentAngle) >= MathF.RadAngle1; if (hasError) { float circleBegin = currentAngle; float circleEnd = targetAngle; if (MathF.TurnDir(circleBegin, circleEnd) < 0) { MathF.Swap(ref circleBegin, ref circleEnd); circleEnd = circleBegin + MathF.CircularDist(circleBegin, circleEnd); } canvas.State.SetMaterial(new BatchInfo(DrawTechnique.Alpha, clrErr)); canvas.DrawLine( bodyPos.X + anchorToWorld.X, bodyPos.Y + anchorToWorld.Y, bodyPos.Z, bodyPos.X + anchorToWorld.X + errorVec.X, bodyPos.Y + anchorToWorld.Y + errorVec.Y, bodyPos.Z); canvas.DrawCircleSegment( bodyPos.X + anchorToWorld.X, bodyPos.Y + anchorToWorld.Y, bodyPos.Z, radius, circleBegin, circleEnd); this.DrawLocalText(canvas, body, string.Format("{0:F0}°", MathF.RadToDeg(MathF.NormalizeAngle(currentAngle))), anchorToWorld + errorVec, Vector2.UnitY, errorVec.Angle); } canvas.State.SetMaterial(new BatchInfo(DrawTechnique.Alpha, clr)); canvas.DrawLine( bodyPos.X + anchorToWorld.X, bodyPos.Y + anchorToWorld.Y, bodyPos.Z, bodyPos.X + anchorToWorld.X + angleVec.X, bodyPos.Y + anchorToWorld.Y + angleVec.Y, bodyPos.Z); this.DrawLocalText(canvas, body, string.Format("{0:F0}°", MathF.RadToDeg(MathF.NormalizeAngle(targetAngle))), anchorToWorld + angleVec, angleVec.Angle); }
private void DrawLocalAngleConstraint(Canvas canvas, RigidBody body, Vector2 anchor, float targetAngle, float currentAngle, float radius) { ColorRgba baseColor = canvas.State.ColorTint; Vector3 bodyPos = body.GameObj.Transform.Pos; radius = this.GetScreenMinScale(canvas, radius, this.minAngleConstraintRadius); float lineWidth = this.GetScreenConstantScale(canvas, this.defaultLineWidth); Vector2 anchorToWorld = body.GameObj.Transform.GetWorldVector(anchor); Vector2 angleVec = Vector2.FromAngleLength(targetAngle, radius); Vector2 errorVec = Vector2.FromAngleLength(currentAngle, radius); bool hasError = MathF.CircularDist(targetAngle, currentAngle) >= MathF.RadAngle1; if (hasError) { float circleBegin = currentAngle; float circleEnd = targetAngle; if (MathF.TurnDir(circleBegin, circleEnd) < 0) { MathF.Swap(ref circleBegin, ref circleEnd); circleEnd = circleBegin + MathF.CircularDist(circleBegin, circleEnd); } canvas.State.ColorTint = baseColor * this.JointErrorColor; canvas.FillThickLine( bodyPos.X + anchorToWorld.X, bodyPos.Y + anchorToWorld.Y, 0.0f, bodyPos.X + anchorToWorld.X + errorVec.X, bodyPos.Y + anchorToWorld.Y + errorVec.Y, 0.0f, lineWidth); canvas.FillCircleSegment( bodyPos.X + anchorToWorld.X, bodyPos.Y + anchorToWorld.Y, 0.0f, radius, circleBegin, circleEnd, lineWidth); } canvas.State.ColorTint = baseColor * this.JointColor; canvas.FillThickLine( bodyPos.X + anchorToWorld.X, bodyPos.Y + anchorToWorld.Y, 0.0f, bodyPos.X + anchorToWorld.X + angleVec.X, bodyPos.Y + anchorToWorld.Y + angleVec.Y, 0.0f, lineWidth); canvas.State.ColorTint = baseColor; }
private void TilesetView_MouseMove(object sender, MouseEventArgs e) { Size tileSize = this.TilesetView.DisplayedTileSize; Point tilePos = this.TilesetView.GetTileIndexLocation(this.TilesetView.HoveredTileIndex); Point posOnTile = new Point(e.X - tilePos.X, e.Y - tilePos.Y); Size centerSize = new Size( tileSize.Width - TilemapsResCache.TilesetCollisionVertical.Width * 2 - 2, tileSize.Height - TilemapsResCache.TilesetCollisionHorizontal.Height * 2 - 2); // Determine the hovered tile hotspot for user interaction TileHotSpot lastHoveredArea = this.hoveredArea; if (posOnTile.X > (tileSize.Width - centerSize.Width) / 2 && posOnTile.Y > (tileSize.Height - centerSize.Height) / 2 && posOnTile.X < (tileSize.Width + centerSize.Width) / 2 && posOnTile.Y < (tileSize.Height + centerSize.Height) / 2) { this.hoveredArea = TileHotSpot.Center; } else { float angle = MathF.Angle(tileSize.Width / 2, tileSize.Height / 2, posOnTile.X, posOnTile.Y); if (MathF.CircularDist(angle, 0.0f) < MathF.RadAngle45) { this.hoveredArea = TileHotSpot.Top; } else if (MathF.CircularDist(angle, MathF.RadAngle90) < MathF.RadAngle45) { this.hoveredArea = TileHotSpot.Right; } else if (MathF.CircularDist(angle, MathF.RadAngle180) < MathF.RadAngle45) { this.hoveredArea = TileHotSpot.Bottom; } else { this.hoveredArea = TileHotSpot.Left; } } // If the user is in the process of setting or clearing bits, perform the drawing operation if (this.isUserDrawing) { this.PerformUserDrawAction(); } if (lastHoveredArea != this.hoveredArea) { this.TilesetView.InvalidateTile(this.TilesetView.HoveredTileIndex, 0); } }
void ICmpUpdatable.OnUpdate() { // Calculate velocity values from last frames movement if (MathF.Abs(Time.TimeMult) > float.Epsilon) { Transform transform = this.GameObj.Transform; Vector3 pos = transform.Pos; float angle = transform.Angle; this.posDiff = pos - this.lastPosition; this.angleDiff = MathF.TurnDir(this.lastAngle, angle) * MathF.CircularDist(this.lastAngle, angle); Vector3 lastVelocity = this.posDiff / Time.TimeMult; float lastAngleVelocity = this.angleDiff / Time.TimeMult; this.velocity += (lastVelocity - this.velocity) * 0.25f * Time.TimeMult; this.angleVelocity += (lastAngleVelocity - this.angleVelocity) * 0.25f * Time.TimeMult; this.lastPosition = pos; this.lastAngle = angle; } }
void ICmpUpdatable.OnUpdate() { Transform transform = this.GameObj.Transform; RigidBody body = this.GameObj.GetComponent <RigidBody>(); SpriteRenderer sprite = this.GameObj.GetComponent <SpriteRenderer>(); float turnDiff = MathF.TurnDir(transform.Angle, this.rotateActivity.Angle) * MathF.CircularDist(transform.Angle, this.rotateActivity.Angle); float thrusterBoost = 0.75f * Vector2.Dot(this.thrusterActivity.Normalized, Vector2.FromAngleLength(transform.Angle, 1.0f)) + 0.25f * this.weaponEnergy; body.AngularVelocity = turnDiff * this.rotateActivity.Length * this.rotationSpeed; body.ApplyWorldForce(this.thrusterActivity * this.thrusterStrength * (1.0f + 0.5f * thrusterBoost) * body.Mass); this.displayedTeamColor = Vector4.Lerp(this.displayedTeamColor, this.teamColor.ToVector(), 0.1f * Time.TimeMult); sprite.ColorTint = this.displayedTeamColor.ToColor(); this.weaponTimer = MathF.Max(0.0f, this.weaponTimer - Time.TimeMult * Time.SPFMult); this.weaponEnergy = MathF.Min(1.0f, this.weaponEnergy + Time.TimeMult * Time.SPFMult / 5.0f); this.health = MathF.Min(1.0f, this.health + Time.TimeMult * Time.SPFMult / 30.0f); if (this == this.GameObj.ParentScene.FindComponent <Player>().ControlTarget) { if (!this.Disposed) { if (this.thrusterSoundInstance == null || this.thrusterSoundInstance.Disposed) { this.thrusterSoundInstance = DualityApp.Sound.PlaySound(this.thrusterSound); this.thrusterSoundInstance.Looped = true; } this.thrusterSoundInstance.Volume = this.thrusterActivity.Length * (0.5f + 0.5f * thrusterBoost); this.thrusterSoundInstance.Pitch = 0.8f + 0.4f * this.thrusterActivity.Length * (0.5f + 0.5f * thrusterBoost); } } }
/// <summary> /// Turns the object to the given absolute radian angle. This will affect the Transforms <see cref="AngleVel">angular velocity</see> value. /// </summary> /// <param name="value"></param> public void TurnToAbs(float value) { this.TurnBy(MathF.TurnDir(this.angleAbs, value) * MathF.CircularDist(value, this.angleAbs)); }
/// <summary> /// Turns the object to the given radian angle in local space of its parent object. This will be treated as movement, rather than teleportation. /// </summary> /// <param name="value"></param> public void TurnToLocal(float value) { this.TurnBy(MathF.TurnDir(this.angle, value) * MathF.CircularDist(value, this.angle)); }
public override void Draw(Canvas target, Vector3 basePos, float baseRotation, float baseScale) { float borderRadius = DefaultOutlineWidth; float circleRadius = this.radius * baseScale; // Scale anti-proportional to perspective scale in order to keep a constant size // in screen space even when actually drawing in world space. { float scale = target.DrawDevice.GetScaleAtZ(this.pos.Z + basePos.Z); borderRadius /= scale; if (this.invariantScale) { circleRadius /= scale; } } // Determine circle position Vector3 circlePos = this.pos; MathF.TransformCoord(ref circlePos.X, ref circlePos.Y, baseRotation, baseScale); circlePos += basePos; // Draw circle target.State.ColorTint *= this.Color; target.FillCircleSegment( circlePos.X, circlePos.Y, circlePos.Z, circleRadius - borderRadius * 0.5f, this.minAngle + baseRotation, this.maxAngle + baseRotation); // Draw circle outline target.State.DepthOffset -= 0.01f; target.State.ColorTint *= ColorRgba.Black; target.FillCircleSegment( circlePos.X, circlePos.Y, circlePos.Z, circleRadius, this.minAngle + baseRotation, this.maxAngle + baseRotation, borderRadius); if (MathF.CircularDist(this.minAngle, this.maxAngle) > MathF.RadAngle1 * 0.001f) { Vector2 minAngleVec = Vector2.FromAngleLength(this.minAngle + baseRotation, circleRadius); Vector2 maxAngleVec = Vector2.FromAngleLength(this.maxAngle + baseRotation, circleRadius); target.FillThickLine( circlePos.X, circlePos.Y, circlePos.Z, circlePos.X + minAngleVec.X, circlePos.Y + minAngleVec.Y, circlePos.Z, borderRadius); target.FillThickLine( circlePos.X, circlePos.Y, circlePos.Z, circlePos.X + maxAngleVec.X, circlePos.Y + maxAngleVec.Y, circlePos.Z, borderRadius); } }
void ICmpUpdatable.OnUpdate() { // Don't have a valid, active animation? Early-out. if (this.activeAnim == null) { return; } if (this.activeAnim.FrameCount <= 0) { return; } if (this.activeAnim.DirectionMap.Length == 0) { return; } // Retrieve the actor renderer we're going to animate ActorRenderer actor = this.GameObj.GetComponent <ActorRenderer>(); // Determine the active direction int startFrame = 0; float minAngleDiff = float.MaxValue; for (int i = 0; i < this.activeAnim.DirectionMap.Length; i++) { float angleDiff = MathF.CircularDist( this.animDirection, MathF.DegToRad(this.activeAnim.DirectionMap[i].Angle)); if (angleDiff < minAngleDiff) { minAngleDiff = angleDiff; startFrame = this.activeAnim.DirectionMap[i].SpriteSheetIndex; } } // Determine the currently displayed frame float animProgress = (this.animTime / this.activeAnim.Duration) % 1.0f; int animCycleCount = (int)(this.animTime / this.activeAnim.Duration); switch (this.activeLoopMode) { // In single-shot animations, complete the animation only once case LoopMode.Once: if (animCycleCount > 1) { animProgress = 0.0f; } goto case LoopMode.Loop; // Regular looped animation case LoopMode.Loop: actor.SpriteIndex = startFrame + MathF.Clamp( (int)(this.activeAnim.FrameCount * animProgress), 0, this.activeAnim.FrameCount); break; // Alternating regular and reverse animation case LoopMode.PingPong: bool reverse = (animCycleCount % 2 == 0); float pingPongAnimProgress = reverse ? (1.0f - animProgress) : animProgress; actor.SpriteIndex = startFrame + MathF.Clamp( (int)(0.5f + (this.activeAnim.FrameCount - 1) * pingPongAnimProgress), 0, this.activeAnim.FrameCount); break; } // Advance animation time, unless we're displaying a fixed single frame if (this.activeLoopMode != LoopMode.RandomSingle) { this.animTime += this.animSpeed * Time.TimeMult * Time.SPFMult; } }
void ICmpUpdatable.OnUpdate() { // Don't have a valid, active animation? Early-out. if (this.activeAnim == null) { return; } if (this.activeAnim.FrameCount <= 0) { return; } if (this.activeAnim.DirectionMap.Length == 0) { return; } // Determine the active direction int startFrame = 0; float minAngleDiff = float.MaxValue; for (int i = 0; i < this.activeAnim.DirectionMap.Length; i++) { float angleDiff = MathF.CircularDist( this.animDirection, MathF.DegToRad(this.activeAnim.DirectionMap[i].Angle)); if (angleDiff < minAngleDiff) { minAngleDiff = angleDiff; startFrame = this.activeAnim.DirectionMap[i].SpriteSheetIndex; } } // Reset animation state this.spriteIndex.Current = 0; this.spriteIndex.Next = 0; this.spriteIndex.Blend = 0.0f; // Determine the currently displayed frames float animProgress = (this.animTime / this.activeAnim.Duration) % 1.0f; int animCycleCount = (int)(this.animTime / this.activeAnim.Duration); switch (this.activeLoopMode) { // In single-shot animations, complete the animation only once case LoopMode.Once: { if (animCycleCount > 1) { animProgress = 0.0f; } goto case LoopMode.Loop; } // Regular looped animation case LoopMode.Loop: { float frameTemp = this.activeAnim.FrameCount * animProgress; this.spriteIndex.Current = startFrame + (int)frameTemp; this.spriteIndex.Next = startFrame + (((int)frameTemp + 1) % this.activeAnim.FrameCount); this.spriteIndex.Blend = frameTemp - (int)frameTemp; break; } // Alternating regular and reverse animation case LoopMode.PingPong: { bool reverse = (animCycleCount % 2 == 0); if (reverse) { float frameTemp = 0.5f + (this.activeAnim.FrameCount - 1) * (1.0f - animProgress); this.spriteIndex.Current = startFrame + (int)frameTemp; this.spriteIndex.Next = startFrame + (((int)frameTemp - 1 + this.activeAnim.FrameCount) % this.activeAnim.FrameCount); this.spriteIndex.Blend = frameTemp - (int)frameTemp; } else { float frameTemp = 0.5f + (this.activeAnim.FrameCount - 1) * animProgress; this.spriteIndex.Current = startFrame + (int)frameTemp; this.spriteIndex.Next = startFrame + (((int)frameTemp + 1) % this.activeAnim.FrameCount); this.spriteIndex.Blend = frameTemp - (int)frameTemp; } break; } } // Apply the updated animation state to the actor / sprite we're animating this.ApplySpriteIndex(); // Advance animation time, unless we're displaying a fixed single frame if (this.activeLoopMode != LoopMode.RandomSingle) { this.animTime += this.animSpeed * Time.TimeMult * Time.SecondsPerFrame; } }
private void DrawLocalAngleConstraint(Canvas canvas, RigidBody body, Vector2 anchor, float minAngle, float maxAngle, float currentAngle, float radius) { Vector3 bodyPos = body.GameObj.Transform.Pos; ColorRgba clr = this.JointColor; ColorRgba clrErr = this.JointErrorColor; Vector2 anchorToWorld = body.GameObj.Transform.GetWorldVector(anchor); Vector2 angleVecMin = Vector2.FromAngleLength(minAngle, radius); Vector2 angleVecMax = Vector2.FromAngleLength(maxAngle, radius); Vector2 errorVec = Vector2.FromAngleLength(currentAngle, radius); float angleDistMin = MathF.Abs(currentAngle - minAngle); float angleDistMax = MathF.Abs(currentAngle - maxAngle); float angleRange = maxAngle - minAngle; bool hasError = angleDistMin > angleDistMax ? angleDistMin >= angleRange : angleDistMax >= angleRange; if (hasError) { float circleBegin = currentAngle; float circleEnd = angleDistMin < angleDistMax ? minAngle : maxAngle; if (MathF.TurnDir(circleBegin, circleEnd) < 0) { MathF.Swap(ref circleBegin, ref circleEnd); circleEnd = circleBegin + MathF.CircularDist(circleBegin, circleEnd); } canvas.State.SetMaterial(new BatchInfo(DrawTechnique.Alpha, clrErr)); canvas.DrawLine( bodyPos.X + anchorToWorld.X, bodyPos.Y + anchorToWorld.Y, bodyPos.Z, bodyPos.X + anchorToWorld.X + errorVec.X, bodyPos.Y + anchorToWorld.Y + errorVec.Y, bodyPos.Z); canvas.DrawCircleSegment( bodyPos.X + anchorToWorld.X, bodyPos.Y + anchorToWorld.Y, bodyPos.Z, radius, circleBegin, circleEnd); this.DrawLocalText(canvas, body, string.Format("{0:F0}°", MathF.RadToDeg(currentAngle)), anchorToWorld + errorVec, Vector2.UnitY, errorVec.Angle); } canvas.State.SetMaterial(new BatchInfo(DrawTechnique.Alpha, clr)); canvas.DrawCircleSegment( bodyPos.X + anchorToWorld.X, bodyPos.Y + anchorToWorld.Y, bodyPos.Z, radius, minAngle, maxAngle); canvas.DrawLine( bodyPos.X + anchorToWorld.X, bodyPos.Y + anchorToWorld.Y, bodyPos.Z, bodyPos.X + anchorToWorld.X + angleVecMin.X, bodyPos.Y + anchorToWorld.Y + angleVecMin.Y, bodyPos.Z); canvas.DrawLine( bodyPos.X + anchorToWorld.X, bodyPos.Y + anchorToWorld.Y, bodyPos.Z, bodyPos.X + anchorToWorld.X + angleVecMax.X, bodyPos.Y + anchorToWorld.Y + angleVecMax.Y, bodyPos.Z); this.DrawLocalText(canvas, body, string.Format("{0:F0}°", MathF.RadToDeg(minAngle)), anchorToWorld + angleVecMin, angleVecMin.Angle); this.DrawLocalText(canvas, body, string.Format("{0:F0}°", MathF.RadToDeg(maxAngle)), anchorToWorld + angleVecMax, angleVecMax.Angle); }
private void TilesetView_MouseMove(object sender, MouseEventArgs e) { Size tileSize = this.TilesetView.DisplayedTileSize; Point tilePos = this.TilesetView.GetTileIndexLocation(this.TilesetView.HoveredTileIndex); Point posOnTile = new Point(e.X - tilePos.X, e.Y - tilePos.Y); Size centerSize = new Size(tileSize.Width / 2, tileSize.Height / 2); // Determine the hovered tile hotspot for user interaction TileConnection lastHoveredArea = this.hoveredArea; if (posOnTile.X > (tileSize.Width - centerSize.Width) / 2 && posOnTile.Y > (tileSize.Height - centerSize.Height) / 2 && posOnTile.X < (tileSize.Width + centerSize.Width) / 2 && posOnTile.Y < (tileSize.Height + centerSize.Height) / 2) { this.hoveredArea = TileConnection.None; } else { float angle = MathF.Angle(tileSize.Width / 2, tileSize.Height / 2, posOnTile.X, posOnTile.Y); float threshold = MathF.DegToRad(22.5f); if (MathF.CircularDist(angle, MathF.DegToRad(315.0f)) <= threshold) { this.hoveredArea = TileConnection.TopLeft; } else if (MathF.CircularDist(angle, MathF.DegToRad(0.0f)) <= threshold) { this.hoveredArea = TileConnection.Top; } else if (MathF.CircularDist(angle, MathF.DegToRad(45.0f)) <= threshold) { this.hoveredArea = TileConnection.TopRight; } else if (MathF.CircularDist(angle, MathF.DegToRad(270.0f)) <= threshold) { this.hoveredArea = TileConnection.Left; } else if (MathF.CircularDist(angle, MathF.DegToRad(90.0f)) <= threshold) { this.hoveredArea = TileConnection.Right; } else if (MathF.CircularDist(angle, MathF.DegToRad(225.0f)) <= threshold) { this.hoveredArea = TileConnection.BottomLeft; } else if (MathF.CircularDist(angle, MathF.DegToRad(180.0f)) <= threshold) { this.hoveredArea = TileConnection.Bottom; } else { this.hoveredArea = TileConnection.BottomRight; } } // Update action state TilesetAutoTileInput autoTile = this.currentAutoTile; if (autoTile != null) { this.isBaseTileDraw = autoTile.BaseTileIndex == -1 || autoTile.BaseTileIndex == this.TilesetView.HoveredTileIndex; } this.UpdateExternalDrawMode(); // If the user is in the process of setting or clearing bits, perform the drawing operation if (this.isUserDrawing) { this.PerformUserDrawAction(); } if (lastHoveredArea != this.hoveredArea) { this.TilesetView.InvalidateTile(this.TilesetView.HoveredTileIndex, 0); } }
void ICmpUpdatable.OnUpdate() { Transform transform = this.GameObj.Transform; RigidBody body = this.GameObj.GetComponent <RigidBody>(); ShipBlueprint blueprint = this.blueprint.Res; // Heal when damaged if (this.hitpoints < 1.0f) { this.hitpoints = MathF.Clamp(this.hitpoints + blueprint.HealRate * Time.SPFMult * Time.TimeMult / blueprint.MaxHitpoints, 0.0f, 1.0f); } // Apply force according to the desired thrust Vector2 actualVelocity = body.LinearVelocity; Vector2 targetVelocity = this.targetThrust * blueprint.MaxSpeed; Vector2 velocityDiff = (targetVelocity - actualVelocity); float sameDirectionFactor = Vector2.Dot( velocityDiff / MathF.Max(0.001f, velocityDiff.Length), this.targetThrust / MathF.Max(0.001f, this.targetThrust.Length)); Vector2 thrusterActivity = this.targetThrust.Length * MathF.Max(sameDirectionFactor, 0.0f) * velocityDiff / MathF.Max(velocityDiff.Length, 1.0f); if (thrusterActivity.Length > 0.00001f) // Don't wake physics without actually doing work { body.ApplyWorldForce(thrusterActivity * blueprint.ThrusterPower); } // Turn to the desired fire angle if (this.targetAngleRatio > 0.0f) { float shortestTurnDirection = MathF.TurnDir(transform.Angle, this.targetAngle); float shortestTurnLength = MathF.CircularDist(transform.Angle, this.targetAngle); float turnDirection; float turnLength; if (MathF.Abs(body.AngularVelocity) > blueprint.MaxTurnSpeed * 0.25f) { turnDirection = MathF.Sign(body.AngularVelocity); turnLength = (turnDirection == shortestTurnDirection) ? shortestTurnLength : (MathF.RadAngle360 - shortestTurnLength); } else { turnDirection = shortestTurnDirection; turnLength = shortestTurnLength; } float turnSpeedRatio = MathF.Min(turnLength * 0.25f, MathF.RadAngle30) / MathF.RadAngle30; float turnVelocity = turnSpeedRatio * turnDirection * blueprint.MaxTurnSpeed * this.targetAngleRatio; float angularVelocityChange = (turnVelocity - body.AngularVelocity) * blueprint.TurnPower; if (MathF.Abs(angularVelocityChange) > 0.0000001f) // Don't wake physics without actually doing work { body.AngularVelocity += angularVelocityChange * Time.TimeMult; } } // Weapon cooldown this.weaponTimer = MathF.Max(0.0f, this.weaponTimer - Time.MsPFMult * Time.TimeMult); // Play the owners special flight sound, when available if (this.owner != null && this.owner.FlightLoop != null) { SoundListener listener = Scene.Current.FindComponent <SoundListener>(); Vector3 listenerPos = listener.GameObj.Transform.Pos; // Determine the target panning manually, because we don't want a true 3D sound here (doppler, falloff, ...) float targetPanning; if (listenerPos.Xy == transform.Pos.Xy || Player.AlivePlayers.Count() <= 1) { targetPanning = 0.0f; } else { targetPanning = -Vector2.Dot(Vector2.UnitX, (listenerPos - transform.Pos).Xy.Normalized); } // Determine the target volume float targetVolume = MathF.Clamp(this.targetThrust.Length, 0.0f, 1.0f); // Clean up disposed flight loop if (this.flightLoop != null && this.flightLoop.Disposed) { this.flightLoop = null; } // Start the flight loop when requested if (targetVolume > 0.0f && this.flightLoop == null) { if ((int)Time.MainTimer.TotalMilliseconds % 2976 <= (int)Time.MsPFMult) { this.flightLoop = DualityApp.Sound.PlaySound(this.owner.FlightLoop); this.flightLoop.Looped = true; } } // Configure existing flight loop if (this.flightLoop != null) { this.flightLoop.Volume += (targetVolume - this.flightLoop.Volume) * 0.05f * Time.TimeMult; this.flightLoop.Panning += (targetPanning - this.flightLoop.Panning) * 0.05f * Time.TimeMult; } } // Display the damage effect when damaged if (this.hitpoints < 0.85f && blueprint.DamageEffect != null) { // Create a new damage effect instance, if not present yet if (this.damageEffect == null) { GameObject damageObj = blueprint.DamageEffect.Res.Instantiate(transform.Pos); damageObj.Parent = this.GameObj; this.damageEffect = damageObj.GetComponent <ParticleEffect>(); if (this.damageEffect == null) { throw new NullReferenceException(); } } // Configure the damage effect foreach (ParticleEmitter emitter in this.damageEffect.Emitters) { if (emitter == null) { continue; } emitter.BurstDelay = new Range(50.0f + this.hitpoints * 50.0f, 100.0f + this.hitpoints * 300.0f); if (this.owner != null) { ColorHsva targetColor = this.owner.Color.ToHsva(); emitter.MinColor = new ColorHsva(targetColor.H, targetColor.S, emitter.MinColor.V, emitter.MinColor.A); emitter.MaxColor = new ColorHsva(targetColor.H, targetColor.S, emitter.MaxColor.V, emitter.MaxColor.A); } } } // Get rid of existing damage effects, if no longer needed else if (this.damageEffect != null) { // Stop emitting and dispose when empty foreach (ParticleEmitter emitter in this.damageEffect.Emitters) { if (emitter == null) { continue; } emitter.BurstDelay = float.MaxValue; } this.damageEffect.DisposeWhenEmpty = true; this.damageEffect = null; } }