Beispiel #1
0
        public LineSeg[] GenerateLineSegs(float height, Vertex2D tangent, IItem item)
        {
            if (_gateData.TwoWay)
            {
                return(new LineSeg[0]);
            }

            var halfLength = _gateData.Length * 0.5f;
            var angleMin   = MathF.Min(_gateData.AngleMin, _gateData.AngleMax);           // correct angle inversions
            var angleMax   = MathF.Max(_gateData.AngleMin, _gateData.AngleMax);

            _gateData.AngleMin = angleMin;
            _gateData.AngleMax = angleMax;

            // oversize by the ball's radius to prevent the ball from clipping through
            var rgv = new[] {
                _gateData.Center.Clone().Add(tangent.Clone().MultiplyScalar(halfLength + PhysicsConstants.PhysSkin)),
                _gateData.Center.Clone().Sub(tangent.Clone().MultiplyScalar(halfLength + PhysicsConstants.PhysSkin)),
            };
            var lineSeg = new LineSeg(rgv[0], rgv[1], height, height + 2.0f * PhysicsConstants.PhysSkin, ItemType.Gate, item);             //!! = ball diameter

            lineSeg.SetElasticity(_gateData.Elasticity);
            lineSeg.SetFriction(_gateData.Friction);

            return(new[] { lineSeg });
        }
Beispiel #2
0
        public HitCircle[] GenerateBracketHits(float height, Vertex2D tangent, IItem item)
        {
            var halfLength = _gateData.Length * 0.5f;

            if (_gateData.ShowBracket)
            {
                return(new[] {
                    new HitCircle(_gateData.Center.Clone().Add(tangent.Clone().MultiplyScalar(halfLength)), 0.01f,
                                  height, height + _gateData.Height, ItemType.Gate, item),
                    new HitCircle(_gateData.Center.Clone().Sub(tangent.Clone().MultiplyScalar(halfLength)), 0.01f,
                                  height, height + _gateData.Height, ItemType.Gate, item)
                });
            }

            return(new HitCircle[0]);
        }
 private TriggerHitLineSeg GetLineSeg(Vertex2D pv1, Vertex2D pv2, float height, IItem item)
 {
     return(new TriggerHitLineSeg(
                pv1.Clone(),
                pv2.Clone(),
                height,
                height + MathF.Max(_data.HitHeight - 8.0f, 0f),          // adjust for same hit height as circular
                item
                ));
 }
 private TriggerHitLineSeg GetLineSeg(Vertex2D pv1, Vertex2D pv2, EventProxy events, float height)
 {
     return(new TriggerHitLineSeg(
                pv1.Clone(),
                pv2.Clone(),
                height,
                height + MathF.Max(_data.HitHeight - 8.0f, 0f)          // adjust for same hit height as circular
                )
     {
         Obj = events
     });
 }
        private List <HitObject> GenerateWallLineSeg(Vertex2D pv1, Vertex2D pv2, bool pv3Exists, float height1, float height2, float wallHeight, IItem item)
        {
            var hitObjects = new List <HitObject>();

            //!! Hit-walls are still done via 2D line segments with only a single lower and upper border, so the wall will always reach below and above the actual ramp -between- two points of the ramp
            // Thus, subdivide until at some point the approximation error is 'subtle' enough so that one will usually not notice (i.e. dependent on ball size)
            if (height2 - height1 > 2.0 * PhysicsConstants.PhysSkin)               //!! use ballsize
            {
                hitObjects.AddRange(GenerateWallLineSeg(pv1, pv1.Clone().Add(pv2).MultiplyScalar(0.5f), pv3Exists, height1, (height1 + height2) * 0.5f, wallHeight, item));
                hitObjects.AddRange(GenerateWallLineSeg(pv1.Clone().Add(pv2).MultiplyScalar(0.5f), pv2, true, (height1 + height2) * 0.5f, height2, wallHeight, item));
            }
            else
            {
                hitObjects.Add(new LineSeg(pv1, pv2, height1, height2 + wallHeight, ItemType.Ramp, item));
                if (pv3Exists)
                {
                    hitObjects.Add(GenerateJoint2D(pv1, height1, height2 + wallHeight, item));
                }
            }
            return(hitObjects);
        }
Beispiel #6
0
        public SpinnerHit(SpinnerData data, float height) : base(ItemType.Spinner)
        {
            var halfLength = data.Length * 0.5f;

            var radAngle = MathF.DegToRad(data.Rotation);
            var sn       = MathF.Sin(radAngle);
            var cs       = MathF.Cos(radAngle);

            var v1 = new Vertex2D(
                data.Center.X - cs * (halfLength + PhysicsConstants.PhysSkin),                 // through the edge of the
                data.Center.Y - sn * (halfLength + PhysicsConstants.PhysSkin)                  // spinner
                );
            var v2 = new Vertex2D(
                data.Center.X + cs * (halfLength + PhysicsConstants.PhysSkin),                 // oversize by the ball radius
                data.Center.Y + sn * (halfLength + PhysicsConstants.PhysSkin)                  // this will prevent clipping
                );

            LineSeg0 = new LineSeg(v1, v2, height, height + 2.0f * PhysicsConstants.PhysSkin, ItemType.Spinner);
            LineSeg1 = new LineSeg(v2.Clone(), v1.Clone(), height, height + 2.0f * PhysicsConstants.PhysSkin, ItemType.Spinner);
        }
        public override float HitTest(Ball ball, float dTime, CollisionEvent coll, PlayerPhysics physics)
        {
            if (!IsEnabled)
            {
                return(-1.0f);
            }

            var bp2d = new Vertex2D(ball.State.Pos.X, ball.State.Pos.Y);
            var dist = bp2d.Clone().Sub(Xy);                                               // relative ball position
            var dv   = new Vertex2D(ball.Hit.Vel.X, ball.Hit.Vel.Y);

            var bcddsq = dist.LengthSq();                                                  // ball center to line distance squared
            var bcdd   = MathF.Sqrt(bcddsq);                                               // distance ball to line

            if (bcdd <= 1.0e-6)
            {
                // no hit on exact center
                return(-1.0f);
            }

            var b   = dist.Dot(dv);
            var bnv = b / bcdd;                                                            // ball normal velocity

            if (bnv > PhysicsConstants.ContactVel)
            {
                // clearly receding from radius
                return(-1.0f);
            }

            var bnd = bcdd - ball.Data.Radius;                                             // ball distance to line
            var a   = dv.LengthSq();

            float hitTime;
            var   isContact = false;

            if (bnd < PhysicsConstants.PhysTouch)
            {
                // already in collision distance?
                if (MathF.Abs(bnv) <= PhysicsConstants.ContactVel)
                {
                    isContact = true;
                    hitTime   = 0f;
                }
                else
                {
                    // estimate based on distance and speed along distance
                    hitTime = -bnd / bnv;
                }
            }
            else
            {
                if (a < 1.0e-8)
                {
                    // no hit - ball not moving relative to object
                    return(-1.0f);
                }

                var sol = Functions.SolveQuadraticEq(a, 2.0f * b, bcddsq - ball.Data.Radius * ball.Data.Radius);
                if (sol == null)
                {
                    return(-1.0f);
                }

                var time1 = sol.Item1;
                var time2 = sol.Item2;

                // find smallest non-negative solution
                hitTime = time1 * time2 < 0 ? MathF.Max(time1, time2) : MathF.Min(time1, time2);
            }

            if (float.IsNaN(hitTime) || float.IsInfinity(hitTime) || hitTime < 0 || hitTime > dTime)
            {
                // contact out of physics frame
                return(-1.0f);
            }

            var hitZ = ball.State.Pos.Z + hitTime * ball.Hit.Vel.Z;                        // ball z position at hit time

            if (hitZ < HitBBox.ZLow || hitZ > HitBBox.ZHigh)
            {
                // check z coordinate
                return(-1.0f);
            }

            var hitX = ball.State.Pos.X + hitTime * ball.Hit.Vel.X;                        // ball x position at hit time
            var hitY = ball.State.Pos.Y + hitTime * ball.Hit.Vel.Y;                        // ball y position at hit time

            var norm = new Vertex2D(hitX - Xy.X, hitY - Xy.Y).Normalize();

            coll.HitNormal.Set(norm.X, norm.Y, 0.0f);

            coll.IsContact = isContact;
            if (isContact)
            {
                coll.HitOrgNormalVelocity = bnv;
            }

            coll.HitDistance = bnd;             // actual contact distance
            //coll.M_hitRigid = true;

            return(hitTime);
        }
        internal RampVertex GetRampVertex(Table.Table table, float accuracy, bool incWidth)
        {
            var result = new RampVertex();

            // vvertex are the 2D vertices forming the central curve of the ramp as seen from above
            var vertex = GetCentralCurve(table, accuracy);

            var numVertices = vertex.Length;

            result.VertexCount  = numVertices;
            result.PointHeights = new float[numVertices];
            result.Cross        = new bool[numVertices];
            result.PointRatios  = new float[numVertices];
            result.MiddlePoints = new Vertex2D[numVertices];
            result.RgvLocal     = new Vertex2D[_data.RampType != RampType.RampTypeFlat ? (numVertices + 1) * 2 : numVertices * 2];

            // Compute an approximation to the length of the central curve
            // by adding up the lengths of the line segments.
            var totalLength  = 0f;
            var bottomHeight = _data.HeightBottom + table.TableHeight;
            var topHeight    = _data.HeightTop + table.TableHeight;

            for (var i = 0; i < numVertices - 1; i++)
            {
                var v1 = vertex[i];
                var v2 = vertex[i + 1];

                var dx     = v1.X - v2.X;
                var dy     = v1.Y - v2.Y;
                var length = MathF.Sqrt(dx * dx + dy * dy);

                totalLength += length;
            }

            var currentLength = 0f;

            for (var i = 0; i < numVertices; i++)
            {
                // clamp next and prev as ramps do not loop
                var prev   = vertex[i > 0 ? i - 1 : i];
                var next   = vertex[i < numVertices - 1 ? i + 1 : i];
                var middle = vertex[i];

                result.Cross[i] = middle.IsControlPoint;

                var normal = new Vertex2D();
                // Get normal at this point
                // Notice that these values equal the ones in the line
                // equation and could probably be substituted by them.
                var v1Normal = new Vertex2D(prev.Y - middle.Y, middle.X - prev.X);                 // vector vmiddle-vprev rotated RIGHT
                var v2Normal = new Vertex2D(middle.Y - next.Y, next.X - middle.X);                 // vector vnext-vmiddle rotated RIGHT

                // special handling for beginning and end of the ramp, as ramps do not loop
                if (i == numVertices - 1)
                {
                    v1Normal.Normalize();
                    normal = v1Normal;
                }
                else if (i == 0)
                {
                    v2Normal.Normalize();
                    normal = v2Normal;
                }
                else
                {
                    v1Normal.Normalize();
                    v2Normal.Normalize();

                    if (MathF.Abs(v1Normal.X - v2Normal.X) < 0.0001 && MathF.Abs(v1Normal.Y - v2Normal.Y) < 0.0001)
                    {
                        // Two parallel segments
                        normal = v1Normal;
                    }
                    else
                    {
                        // Find intersection of the two edges meeting this points, but
                        // shift those lines outwards along their normals

                        // First line
                        var a = prev.Y - middle.Y;
                        var b = middle.X - prev.X;

                        // Shift line along the normal
                        var c = -(a * (prev.X - v1Normal.X) + b * (prev.Y - v1Normal.Y));

                        // Second line
                        var d = next.Y - middle.Y;
                        var e = middle.X - next.X;

                        // Shift line along the normal
                        var f = -(d * (next.X - v2Normal.X) + e * (next.Y - v2Normal.Y));

                        var det    = a * e - b * d;
                        var invDet = det != 0.0 ? 1.0f / det : 0.0f;

                        var intersectX = (b * f - e * c) * invDet;
                        var intersectY = (c * d - a * f) * invDet;

                        normal.X = middle.X - intersectX;
                        normal.Y = middle.Y - intersectY;
                    }
                }

                // Update current length along the ramp.
                var dx     = prev.X - middle.X;
                var dy     = prev.Y - middle.Y;
                var length = MathF.Sqrt(dx * dx + dy * dy);

                currentLength += length;

                var percentage   = currentLength / totalLength;
                var currentWidth = percentage * (_data.WidthTop - _data.WidthBottom) + _data.WidthBottom;
                result.PointHeights[i] = middle.Z + percentage * (topHeight - bottomHeight) + bottomHeight;

                AssignHeightToControlPoint(vertex[i], middle.Z + percentage * (topHeight - bottomHeight) + bottomHeight);
                result.PointRatios[i] = 1.0f - percentage;

                // only change the width if we want to create vertices for rendering or for the editor
                // the collision engine uses flat type ramps
                if (IsHabitrail() && _data.RampType != RampType.RampType1Wire)
                {
                    currentWidth = _data.WireDistanceX;
                    if (incWidth)
                    {
                        currentWidth += 20.0f;
                    }
                }
                else if (_data.RampType == RampType.RampType1Wire)
                {
                    currentWidth = _data.WireDiameter;
                }

                result.MiddlePoints[i] = new Vertex2D(middle.X, middle.Y).Add(normal);
                result.RgvLocal[i]     = new Vertex2D(middle.X, middle.Y).Add(normal.Clone().MultiplyScalar(currentWidth * 0.5f));
                result.RgvLocal[numVertices * 2 - i - 1] = new Vertex2D(middle.X, middle.Y).Sub(normal.Clone().MultiplyScalar(currentWidth * 0.5f));
            }

            return(result);
        }