tweak initial live radar

This commit is contained in:
RaidMax 2019-07-02 17:30:05 -05:00
parent b64bce2936
commit 665218f641
3 changed files with 175 additions and 50 deletions

View File

@ -21,6 +21,23 @@ namespace LiveRadar
public int Health { get; set; } public int Health { get; set; }
public bool IsAlive { get; set; } public bool IsAlive { get; set; }
public Vector3 RadianAngles => new Vector3(ViewAngles.X.ToRadians(), ViewAngles.Y.ToRadians(), ViewAngles.Z.ToRadians()); public Vector3 RadianAngles => new Vector3(ViewAngles.X.ToRadians(), ViewAngles.Y.ToRadians(), ViewAngles.Z.ToRadians());
public RadarEvent Previous { get; set; }
public int Id => GetHashCode();
public override bool Equals(object obj)
{
if (obj is RadarEvent re)
{
return re.ViewAngles.X == ViewAngles.X &&
re.ViewAngles.Y == ViewAngles.Y &&
re.ViewAngles.Z == ViewAngles.Z &&
re.Location.X == Location.X &&
re.Location.Y == Location.Y &&
re.Location.Z == Location.Z;
}
return false;
}
public static RadarEvent Parse(string input) public static RadarEvent Parse(string input)
{ {
@ -28,9 +45,9 @@ namespace LiveRadar
var parsedEvent = new RadarEvent() var parsedEvent = new RadarEvent()
{ {
Guid = items[0].ConvertLong(), Guid = items[0].ConvertGuidToLong(),
Location = Vector3.Parse(items[1]), Location = Vector3.Parse(items[1]),
ViewAngles = Vector3.Parse(items[2]), ViewAngles = Vector3.Parse(items[2]).FixIW4Angles(),
Team = items[3], Team = items[3],
Kills = int.Parse(items[4]), Kills = int.Parse(items[4]),
Deaths = int.Parse(items[5]), Deaths = int.Parse(items[5]),

View File

@ -38,20 +38,28 @@ namespace LiveRadar.Web.Controllers
{ {
var server = serverId == null ? Manager.GetServers()[0] : Manager.GetServers().First(_server => _server.GetHashCode() == serverId); var server = serverId == null ? Manager.GetServers()[0] : Manager.GetServers().First(_server => _server.GetHashCode() == serverId);
var radarInfo = server.GetClientsAsList().Select(_client => _client.GetAdditionalProperty<RadarEvent>("LiveRadar")); var radarInfo = server.GetClientsAsList().Select(_client => _client.GetAdditionalProperty<RadarEvent>("LiveRadar"));
return Json(radarInfo); return Json(radarInfo);
} }
[HttpGet] [HttpGet]
public IActionResult Update(string payload) public IActionResult Update(string payload)
{ {
return Ok();
var radarUpdate = RadarEvent.Parse(payload); var radarUpdate = RadarEvent.Parse(payload);
var client = Manager.GetActiveClients().First(_client => _client.NetworkId == radarUpdate.Guid); var client = Manager.GetActiveClients().FirstOrDefault(_client => _client.NetworkId == radarUpdate.Guid);
radarUpdate.Name = client.Name;
client.SetAdditionalProperty("LiveRadar", radarUpdate); if (client != null)
{
radarUpdate.Name = client.Name;
var previous = client.GetAdditionalProperty<RadarEvent>("LiveRadar");
// this prevents us from creating a never ending linked list
if (previous != null)
{
previous.Previous = null;
}
radarUpdate.Previous = previous;
client.SetAdditionalProperty("LiveRadar", radarUpdate);
}
return Ok(); return Ok();
} }

View File

@ -8,8 +8,9 @@
@section scripts { @section scripts {
<script> <script>
let map = undefined; const textOffset = 15;
let radarItem = undefined; let previousRadarData = undefined;
let newRadarData = undefined;
function drawCircle(context, x, y, color) { function drawCircle(context, x, y, color) {
context.beginPath(); context.beginPath();
@ -18,18 +19,16 @@
context.fill(); context.fill();
context.lineWidth = 3; context.lineWidth = 3;
context.strokeStyle = 'rgba(255, 255, 255, 0.5)'; context.strokeStyle = 'rgba(255, 255, 255, 0.5)';
context.closePath();
context.stroke(); context.stroke();
} }
function drawLine(context, x1, y1, x2, y2, color) { function drawLine(context, x1, y1, x2, y2, color) {
context.beginPath(); context.beginPath();
context.lineWidth = '1'; context.lineWidth = '3';
var grad = context.createLinearGradient(x1, y1, x2, y2);
grad.addColorStop(0, 'rgba(0, 255, 0, 0.75)');
grad.addColorStop(0.75, 'rgba(223, 66, 244, 0.8)');
context.strokeStyle = grad;
context.moveTo(x1, y1); context.moveTo(x1, y1);
context.lineTo(x2, y2); context.lineTo(x2, y2);
context.closePath();
context.stroke(); context.stroke();
} }
@ -38,16 +37,27 @@
context.moveTo(v1.x, v1.y); context.moveTo(v1.x, v1.y);
context.lineTo(v2.x, v2.y); context.lineTo(v2.x, v2.y);
context.lineTo(v3.x, v3.y); context.lineTo(v3.x, v3.y);
context.closePath(); context.closePath();
context.fillStyle = color; context.fillStyle = color;
context.fill(); context.fill();
} }
function drawText(context, x, y, text, size, fillColor, strokeColor) {
context.beginPath();
context.font = `bold ${size}px open sans`;
context.fillStyle = fillColor;
context.strokeStyle = strokeColor;
context.lineWidth = '0.5';
context.textAlign = 'center';
context.fillText(text, x, y);
context.strokeText(text, x, y);
context.closePath();
}
function checkCanvasSize(canvas, context, minimap, map) { function checkCanvasSize(canvas, context, minimap, map) {
var height = minimap.height() - map.top - map.bottom; var height = minimap.height() - map.top - map.bottom;
var width = minimap.width() - map.left - map.right; var width = minimap.width() - map.left - map.right;
if (context.canvas.height != height || context.canvas.width != width) { if (context.canvas.height != height || context.canvas.width != width) {
context.canvas.height = height; context.canvas.height = height;
context.canvas.width = width; context.canvas.width = width;
@ -66,57 +76,147 @@
return { x: nx * distance, y: ny * distance, z: nz * distance}; return { x: nx * distance, y: ny * distance, z: nz * distance};
} }
function lerp(start, end, complete) {
return (1 - complete) * start + complete * end;
}
function easeLerp(start, end, t) {
let t2 = (1 - Math.cos(t * Math.PI)) / 2;
return (start * (1-t2) + end * t2);
}
function fixRollAngles(oldAngles, newAngles) {
let newX = newAngles.x;
let newY = newAngles.y;
let angleDifferenceX = (oldAngles.x - newAngles.x);
if (angleDifferenceX > Math.PI) {
newX = oldAngles.x + (Math.PI * 2) - angleDifferenceX;
}
else if (Math.abs(newAngles.x - oldAngles.x) > Math.PI) {
newX = newAngles.x - (Math.PI * 2);
}
let angleDifferenceY = (oldAngles.y - newAngles.y);
if (angleDifferenceY > Math.PI) {
newY = oldAngles.y + (Math.PI * 2) - angleDifferenceY;
}
else if (Math.abs(newAngles.y - oldAngles.y) > Math.PI) {
newY = newAngles.y - (Math.PI * 2);
}
return { x: newX, y: newY };
}
const stateInfo = {
canvas: $('#map_canvas'),
ctx: $('#map_canvas')[0].getContext('2d'),
updateFrequency: 500,
updateFrameTimeDeviation: 0,
forwardDistance: undefined,
fovWidth: undefined,
mapInfo: undefined,
mapScaler: undefined
};
function updateRadarData() { function updateRadarData() {
$.getJSON('@Url.Action("Data", "Radar", null)', function (_radarItem) { $.getJSON('@Url.Action("Data", "Radar", null)', function (_radarItem) {
radarItem = _radarItem; newRadarData = _radarItem;
}); });
$.each(newRadarData, function (index, value) {
if (previousRadarData != undefined && index < previousRadarData.length) {
let previous = previousRadarData[index];
// this happens when the player has first joined and we haven't gotten two snapshots yet
if (value == null || previous == null) {
return;
}
// we haven't gotten a new item, it's just the old one again
if (previous.id === value.id) {
value.animationTime = previous.animationTime;
value.previous = value;
}
}});
// we switch out the items to
previousRadarData = newRadarData;
checkCanvasSize(stateInfo.canvas, stateInfo.ctx, $('#map_list'), stateInfo.mapInfo)
if (stateInfo.forwardDistance == undefined) {
stateInfo.mapScaler = stateInfo.mapInfo.width / stateInfo.canvas.width();
stateInfo.forwardDistance = 500.0 / stateInfo.mapScaler;
stateInfo.fovWidth = 32.5 / stateInfo.mapScaler;
}
} }
function updateMap() { function updateMap() {
let canvas = $('#map_canvas'); let ctx = stateInfo.ctx;
let ctx = undefined;
let mapImage = $('#map_list');
if (canvas[0].getContext) {
ctx = canvas[0].getContext('2d');
}
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
checkCanvasSize(canvas, ctx, mapImage, map) now = performance.now();
let forwardDistance = 500.0 / (map.width / canvas.width());
let fovWidth = 32.5 / (map.width / canvas.width()) ;
$.each(previousRadarData, function (index, value) {
if (value == null || value.previous == null) {
console.log("skipping null snapshot");
return;
}
$.each(radarItem, function (index, value) { // this indicates we got a new snapshot to work with so we set the time based off the previous
// frame deviation to have minimal interpolation skipping
if (value.animationTime === undefined) {
value.animationTime = now - stateInfo.updateFrameTimeDeviation;
}
// todo: fix this naming up const elapsedFrameTime = now - value.animationTime;
let xPos = (map.maxLeft - value.location.y) / (map.width / canvas.width()); const completionPercent = elapsedFrameTime / stateInfo.updateFrequency;
let yPos = (map.maxTop - value.location.x) / (map.height / canvas.height());
let color = value.team == 'allies' ? 'rgba(0, 122, 204, 1)' : 'rgba(255,69,69,.85)'; const startX = (stateInfo.mapInfo.maxLeft - value.previous.location.y) / stateInfo.mapScaler;
const startY = (stateInfo.mapInfo.maxTop - value.previous.location.x) / stateInfo.mapScaler;
const endX = (stateInfo.mapInfo.maxLeft - value.location.y) / stateInfo.mapScaler;
const endY = (stateInfo.mapInfo.maxTop - value.location.x) / stateInfo.mapScaler;
let teamColor = value.team == 'allies' ? 'rgba(0, 122, 204, 1)' : 'rgba(255,69,69,.85)';
let fovColor = value.team == 'allies' ? 'rgba(0, 122, 204, 0.25)' : 'rgba(255,69,69,.25)'; let fovColor = value.team == 'allies' ? 'rgba(0, 122, 204, 0.25)' : 'rgba(255,69,69,.25)';
let firstVertex = calculateViewPosition(((Math.PI * 2 * 0.75) - value.radianAngles.y) - fovWidth, value.radianAngles.x, forwardDistance); const rollAngleFix = fixRollAngles(value.previous.radianAngles, value.radianAngles);
let secondVertex = calculateViewPosition(((Math.PI * 2 * 0.75) - value.radianAngles.y) + fovWidth, value.radianAngles.x, forwardDistance);
drawCircle(ctx, xPos, yPos, color); const radianLerpX = lerp(value.previous.radianAngles.x, rollAngleFix.x, completionPercent);
drawTriangle(ctx, { x: xPos, y: yPos }, { x: xPos + firstVertex.x, y: yPos + firstVertex.y }, { x: xPos + secondVertex.x, y: yPos + secondVertex.y }, fovColor); const radianLerpY = lerp(value.previous.radianAngles.y, rollAngleFix.y, completionPercent);
ctx.font = 'bold 24px segoe ui'; // this is some jankiness to get the fov to point the right direction
ctx.fillStyle = 'white'; let firstVertex = calculateViewPosition((Math.PI * 2 * 0.75) - radianLerpX - (Math.PI + stateInfo.fovWidth), radianLerpY, stateInfo.forwardDistance);
ctx.strokeStyle = color; let secondVertex = calculateViewPosition((Math.PI * 2 * 0.75) - radianLerpX + (Math.PI + stateInfo.fovWidth), radianLerpY, stateInfo.forwardDistance);
ctx.lineWidth = '0.5';
ctx.textAlign = 'center'; let currentX = lerp(startX, endX, completionPercent);
ctx.fillText(value.name, xPos, yPos - 15); let currentY = lerp(startY, endY, completionPercent);
ctx.strokeText(value.name, xPos, yPos - 15);
drawCircle(ctx, currentX, currentY, teamColor);
drawTriangle(ctx,
{ x: currentX, y: currentY },
{ x: currentX + firstVertex.x, y: currentY + firstVertex.y },
{ x: currentX + secondVertex.x, y: currentY + secondVertex.y },
fovColor);
drawText(ctx, currentX, currentY - textOffset, value.name, 24, 'white', teamColor)
}); });
window.requestAnimationFrame(updateMap); window.requestAnimationFrame(updateMap);
} }
$.getJSON('@Url.Action("Map", "Radar", null)', function (_map) { $.getJSON('@Url.Action("Map", "Radar", null)', function (_map) {
let div = $('#map_list').append(`<img src=../images/compass_map_${_map.name}@('@')2x.png></img>`); $('#map_list').append(`<img src=../images/compass_map_${_map.name}@('@')2x.png></img>`);
map = _map stateInfo.mapInfo = _map
setInterval(updateRadarData, 250); setInterval(updateRadarData, stateInfo.updateFrequency);
window.requestAnimationFrame(updateMap); window.requestAnimationFrame(updateMap);
}); });
</script> </script>
} }