// ****************************************************************************************** // FIELD SOLVING FUNCTIONS // ****************************************************************************************** private void computeDensityField(CC_Unit ccu) { // grab properties of the CC Unit var footprint = ccu.GetFootprint().Rotate(-ccu.Rotation()); var anchor = ccu.Position(); // compute the footprint's half-dimensions var xHalf = footprint.GetLength(0) / 2f; var yHalf = footprint.GetLength(1) / 2f; // translate the anchor so it's centered about our unit anchor -= new Vector2(xHalf, yHalf); // finally, perform bilinear interpolation of the footprint at our anchor var footprintInterp = footprint.BilinearInterpolation(anchor); // offsets - floor produces smoothest interpolated position stamps var xOffset = Mathf.FloorToInt(anchor.x); var yOffset = Mathf.FloorToInt(anchor.y); // scan the grid, stamping the footprint onto the tile for (int x = 0; x < footprintInterp.GetLength(0); x++) { for (int y = 0; y < footprintInterp.GetLength(1); y++) { // get the rho value float rho = footprintInterp[x, y]; // only perform storage functions if there is a density value if (rho > 0) { var xIndex = x + xOffset; var yIndex = y + yOffset; // add rho to the in-place density addDataToPoint_rho(xIndex, yIndex, rho); } } } }
// ********************************************************************** // Predictive velocity fields // ********************************************************************** private void applyPredictiveVelocity(CC_Unit ccu) { // TODO: Only apply predictive velocity to continuous segments, ie. // if a portion of this predictive path is blocked by impassable // terrain, we should not apply predictive velocity beyond that // point // fetch unit properties var speed = ccu.Speed(); // compute values var distance = (int)Math.Ceiling(speed * CCValues.S.v_predictiveSeconds); var footprint = ccu.GetFootprint(); var height = footprint.GetLength(1); // (1) create a rect with Length = predictive distance, Height = Unit footprint height //var footprintEnd = (int)Math.Floor(footprint.GetLength(0) / 2f); var footprintEnd = Mathf.FloorToInt(ccu.Falloff() + ccu.SizeX - 1); var predictive = new float[footprintEnd + distance, height]; // (2) build half of the footprint into the predictive rect for (int i = 0; i < footprintEnd; i++) { for (int k = 0; k < height; k++) { predictive[i, k] = footprint[i, k]; } } // (3a) record the "vertical slice" of the unit footprint var slice = new float[height]; for (int i = 0; i < slice.Length; i++) { slice[i] = footprint[footprintEnd, i]; } // (3b) scale the vertical slice along the length of the rect // determine falloff rates var start = (CCValues.S.f_rhoMax + CCValues.S.f_rhoMin) / 2; var end = 0f; // CCValues.S.f_rhoMin / 4f; // track iteration int c = 0; for (int i = footprintEnd; i < predictive.GetLength(0); i++) { // taper from <start> down to <end> var scalar = (end - start) / distance * c + start; c++; for (int k = 0; k < height; k++) { // build the predictive velocity rect in front of the footprint predictive[i, k] = slice[k] * scalar; } } // (4) rotate the rect var yEuler = ccu.Rotation(); // Unity y-euler rotations start at +z (+y in 2D) and move CW. // Academic rotations are described as CCW from the +x axis, which is what // many of our derivations are based, so we convert here. var degrees = Mathf.Repeat(90 - yEuler, 360); var radians = degrees * Mathf.Deg2Rad; var rotated = predictive.Rotate(degrees); // (5) determine anchor position - do this by taking the "perfect" center // and applying the same translations/rotations that our rotate process // applies // (i) declare unit location in original footprint center var unitOffset = new Vector2(footprint.GetLength(0) / 2f, height / 2f); // (ii) translate by predictive velocity half-shape to center on (0,0) unitOffset += new Vector2(-predictive.GetLength(0) / 2f, -height / 2f); // (iii) rotate the point about (0,0) by our unit's rotation unitOffset = unitOffset.Rotate(radians); // (iv) translate back by rotated shape half-space unitOffset += new Vector2(rotated.GetLength(0) / 2f, rotated.GetLength(1) / 2f); // finally, translate the anchor to be positioned on the unit var anchor = ccu.Position() - unitOffset; // (6) inteprolate the final result var final = rotated.BilinearInterpolation(anchor); // offsets - floor produces smoothest interpolated position stamps var xOffset = Mathf.FloorToInt(anchor.x); var yOffset = Mathf.FloorToInt(anchor.y); // (7) add the density and velocity along the length of the path, // scaling each by the value of the rect var direction = new Vector2(1, 0).Rotate(radians); var velocity = direction * speed; for (int x = 0; x < final.GetLength(0); x++) { for (int y = 0; y < final.GetLength(1); y++) { var xIndex = x + xOffset; var yIndex = y + yOffset; // add rho and velocity to existing data addDataToPoint_rho(xIndex, yIndex, final[x, y]); addDataToPoint_vAve(xIndex, yIndex, final[x, y] * velocity); } } }