tweak initial live radar
This commit is contained in:
parent
b64bce2936
commit
665218f641
@ -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]),
|
||||||
|
@ -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;
|
|
||||||
|
|
||||||
|
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);
|
client.SetAdditionalProperty("LiveRadar", radarUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
@ -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,56 +76,146 @@
|
|||||||
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>
|
||||||
|
Loading…
Reference in New Issue
Block a user