//helper function to determine all viable snap candidates given an actor and a starting position // will automatically reject candidates outside the configured min snap offset // will also automatically weight the previous frame's results higher to reduce jitter between two close snap points private void AddToCandidates(GameThing thing, Vector3 snapFromPosition, int sourceIndex, ref List <CandidateConnection> snapCandidates) { //loop over all game things looking for pipes for (int i = 0; i < InGame.inGame.gameThingList.Count; i++) { GameActor actor = InGame.inGame.gameThingList[i] as GameActor; if (actor != null && actor.Movement != null && actor != (thing as GameActor)) { if (actor.Chassis != null && actor.Chassis is PipeChassis) { //found a pipe that isn't ourself, look for candidates PipeChassis targetPipe = actor.Chassis as PipeChassis; List <PipeConnection> potentialSnapPoints = GenerateConnections(actor, actor.Movement.Position); //weight each candidate based on how far away it is - if too far, reject it completely foreach (PipeConnection nextSnapPoint in potentialSnapPoints) { Vector3 snapVector = nextSnapPoint.SourcePos - snapFromPosition; snapVector.Z = 0.0f; float distance = snapVector.Length(); //if distance is in max offset or, if it's exactly where we snapped to last time, allow for double the radius to consider it valid if (distance <= kMaxSnapOffset * Parent.ReScale || (m_bIsSnapped && m_lastSnapSourceIndex == sourceIndex && distance < kMaxSnapOffset * Parent.ReScale * 2.0f)) { float weight = 1.0f - MathHelper.Clamp(distance / kMaxSnapOffset, 0.0f, 1.0f); bool retainSnap = false; //special case: were we previously snapped to this exact target, from the same source? // if so, add some weight - we'd prefer to stay in place if (m_bIsSnapped && m_lastSnapSourceIndex == sourceIndex) { float distanceToLastSnapped = (m_lastSnapTarget - nextSnapPoint.SourcePos).Length(); if (distanceToLastSnapped < 0.001f) { weight += 1.0f; retainSnap = true; } } //found a candidate, add it to the lsit CandidateConnection newCandidate = new CandidateConnection { SourceConnectionIndex = sourceIndex, SnapToPosition = nextSnapPoint.SourcePos, UpDir = nextSnapPoint.SourceUpDir, CenterPosition = actor.Movement.Position, Weight = weight, RetainSnap = retainSnap }; snapCandidates.Add(newCandidate); } } } } } }
//helper function that, given an actor and a starting position, will generate valid outgoing pipe connections private List <PipeConnection> GenerateConnections(GameActor actor, Vector3 fromPosition) { List <PipeConnection> connections = new List <PipeConnection>(); PipeChassis chassis = actor.Chassis as PipeChassis; if (chassis == null) { return(connections); } //determine modified direction vectors based on rotation Matrix rotationMat = Matrix.CreateRotationY(chassis.m_terrainPitch) * //rotation due to pitch from terrain in X (rotated around Y) Matrix.CreateRotationX(-chassis.m_terrainRoll) * //rotation due to roll from terrain in Y (rotated around X) Matrix.CreateRotationZ(chassis.m_snappedYaw); //normal rotation due to direction facing Vector3 facingDir = Vector3.TransformNormal(new Vector3(1.0f, 0.0f, 0.0f), rotationMat); facingDir.Normalize(); Vector3 rightDir = Vector3.TransformNormal(new Vector3(0.0f, -1.0f, 0.0f), rotationMat); rightDir.Normalize(); Vector3 upDir = Vector3.TransformNormal(new Vector3(0.0f, 0.0f, 1.0f), rotationMat); upDir.Normalize(); //from those modified direction vectors, determine potential snap points Vector3 forwardPos = fromPosition + facingDir * kGridSize * actor.ReScale; Vector3 backwardPos = fromPosition - facingDir * kGridSize * actor.ReScale; Vector3 rightPos = fromPosition + rightDir * kGridSize * actor.ReScale; Vector3 leftPos = fromPosition - rightDir * kGridSize * actor.ReScale; //add connections based on pipe type PipeConnection nextConnection; switch (chassis.PipeType) { case PipeTypeEnum.PipeStraight: //forward nextConnection = new PipeConnection { SourcePos = forwardPos, SourceUpDir = upDir }; connections.Add(nextConnection); //backward nextConnection = new PipeConnection { SourcePos = backwardPos, SourceUpDir = upDir }; connections.Add(nextConnection); break; case PipeTypeEnum.PipeCross: //forward nextConnection = new PipeConnection { SourcePos = forwardPos, SourceUpDir = upDir }; connections.Add(nextConnection); //backward nextConnection = new PipeConnection { SourcePos = backwardPos, SourceUpDir = upDir }; connections.Add(nextConnection); //left nextConnection = new PipeConnection { SourcePos = leftPos, SourceUpDir = upDir }; connections.Add(nextConnection); //right nextConnection = new PipeConnection { SourcePos = rightPos, SourceUpDir = upDir }; connections.Add(nextConnection); break; case PipeTypeEnum.PipeCorner: //backward nextConnection = new PipeConnection { SourcePos = backwardPos, SourceUpDir = upDir }; connections.Add(nextConnection); //right nextConnection = new PipeConnection { SourcePos = rightPos, SourceUpDir = upDir }; connections.Add(nextConnection); break; } return(connections); }