Welcome to Keen Software House Forums! Log in or Sign up to interact with the KSH community.
  1. You are currently browsing our forum as a guest. Create your own forum account to access all forum functionality.

Space scanner (scans objects and tracks moving ships)

Discussion in 'Programming Released Codes' started by Innoble, Dec 20, 2015.

Thread Status:
This last post in this thread was made more than 31 days old.
  1. Pennywise Apprentice Engineer

    Messages:
    338
    Yes, when it finds many grids and place em in grid list, script becomes very laggy. Thats all because script tracks all found objects. But in most situations you don't need to track so much. I hardly can imagine a real situation in game, when you have more then 10 grids in range.
    I've tested this script in local games and on dedicated servers. It works fine. Sometimes additional delays are needed to slow script down and save server simspeed. But mostly it runs smooth w/o any modifications.
    That's example of modified script test. I've added just few features, allowing me to scan narrow sector in a very long range (over 2 000 000m).

    Vid is not in english, but you can turn sound off and guess what's happening from picture on 4:20. This script is capable to scan martian bases from Earth.
    On 7:30 I'm testing the same script with additional delay. It scans Mars without any drop of simspeed.
     
    Last edited: Apr 10, 2016
  2. Pennywise Apprentice Engineer

    Messages:
    338
    By the way, I've finally found, what does this peece of code, calculating target radius, mean;):
    Code:
       double k = (RT_RF1.Dot(RO_norm) - Math.Sqrt(Sq(RT_RF1.Dot(RO_norm))-4*RT.Dot(RF1)))/2;
       radius = (T- R - k * RO_norm).Length() - beam_width;  
    I've learned at school so long time ago, that really forgotten how to solve square equations.
     
  3. Innoble Apprentice Engineer

    Messages:
    238
    Hey Pennywise!

    I missed your earlier messages (have been on a SE break for a bit, waiting for new features and such). I like how you're experimenting with my script. Please do whatever you want to do with it, no need to ask for my permission.

    Are there still things about the code unclear? I think I left this question unanswered:

    What I am doing (if I remember correctly) is: I check if one of the previously discovered targets is being hit with the default beam, then I make the target smaller (= reducing the beam width) so that the beam can pass without hitting it.

    You have to remember that what we call "beam width" is not actually beam width. It is a way of blowing up the target instead of the beam. The beam is always of 0 width, but the targets can be made larger and smaller. If i have a beam width of 100, it means all targets are 100m larger than their actual size.

    So what I do is, I check with the regular beam width, then if I would hit something that is known, I lower the size of the target just enough that it doesn't get hit. I do this for every target to make sure none of the known targets get hit. If this is not possible (because the target location is blocked even with the 0 beam width option) then the current random target is skipped and another is tried.

    As for the script slowing down the game, I think there are two main contributors and I am not sure which is worse.

    The first
    is the GFD calls. That is internal to the game and not something I can control with my script. I call GFD 3 times per sim tick and the more grids that are in range, the worse the effect on the simulation. If you lower the range, it will be a lot less of a problem.

    The second are the loops inside the script. I check a lot of known targets and such. This effect also becomes weaker with less targets in range. You can change the variable "max loops" to control this slowdown. If you keep this low, it should disappear.

    I am not sure which of these is worse, but I think it is the GFD calls. There is an easy way to test this. If you start the scan and in immediately slows down the simulation, then it is due to the GFD calls, because they are slow from the start. If it gets slower, the more targets are being logged by my script, then the script is the culprit. This is because GFD knows all targets on the server and is immediately slowed down by them (if in range) and my script still has to find them before being bogged down by them.

    Regardless, PennyWise is right, you need to be careful in using it in crowded places. Start with a small range and only use a big range when the server is pretty empty. You can even automate this! Improve the script to start at a small range and slowly increase its range until a maximum number of targets have been found.

    EDIT: And yea you can also use it on a focused area (like scanning faraway Mars) instead of randomly all around. That does stop the slowdown from loops in my script, but it does not stop the slowdown from the GFD calls. GFD calls check all targets in a sphere, always. It just won't trigger on anything you're not aiming at (but it will still slow you down). Thankfully there are only 3 of those every sim tick.
     
    Last edited: Apr 10, 2016
  4. DaMightyMage Apprentice Engineer

    Messages:
    100
    I didn't see any improvement by reducing the range, but that may have simply been because my test world was so cluttered. I'll play around with it in a few other worlds.
    In the meantime, I wrote a simple script that only makes GFD calls, and nothing else, to characterize the drain on system resources. Here's what I found... unfortunately the graph doesn't display properly here:
    https://photos.google.com/share/AF1...?key=b1pudUFVcHpTMXBISFBXQ1ZMcklFaU5yNGx1S3Z3

    I tested calling GFD every tick, every other tick, and every three ticks to see if there's any performance boost of using one method over the other (and there is a tiny improvement with the latter two options).

    I am working on a scanner like this one that is a little more optimized for 360-degree scanning (no separate updates for known contacts, variable scan resolution). You gave me some great ideas for optimizing tricks. Thanks mate! :D
     
    Last edited: Apr 10, 2016
  5. Pennywise Apprentice Engineer

    Messages:
    338
    Thnx, @Innoble:). I'm permanently surpised, how much new or old well-forgotten stuff from math, geometry and physics this game brings to you when you try to script something.
    For instance that "sinus trick" with vector cross function.
     
  6. Pennywise Apprentice Engineer

    Messages:
    338
    You can try to set "int max_targets = 50" to lower value. It will decrease system load even in "space junkyard" worlds.
     
  7. Innoble Apprentice Engineer

    Messages:
    238
    I don't think max targets does anything. I planned on adding that, but the variable is not used in my version of the script. Maybe it is in your version.

    Very true. I teach physics for a living, which helps me a lot when scripting and designing ships. I also use mathematics a lot in my job, but usually not at the level we apply in space engineers. It is a very cool excuse to brush up on linear algebra
     
  8. Wellstat Apprentice Engineer

    Messages:
    212
    Been away from SE for a few months, heard lots of mentioning of GFD and its lagginess. Guess its getting very popular.
     
  9. Innoble Apprentice Engineer

    Messages:
    238
    Yea it is, mostly due to lack of a good long-range sensor. I'm holding off on expanding my GFD code to see what they come up with. Working on learning modding atm. Finally got my evilsensor mod working:)
     
  10. DaMightyMage Apprentice Engineer

    Messages:
    100
    I took the liberty of modifying Innoble's Space Scanner script to be less resource-intensive, with an adjustable delay between GFD calls. It isn't perfect, but it seems to work reasonably well. The code is below if anyone is interested.

    Code:
    // GetFreeDestination Radar
    // Created by Innoble
    // Modified by DaMightyMage
    
    
    int max_GFD = 3;            // 3 is the minimum required for now. You can set it higher, but it may tax the server.
    int max_targets = 100;      // this does not do anything for now. Be aware that having a high number of grids will lag
    int asteroid_check = 18000; // check an asteroid every 15 minutes. Usually it will be hit by a random beam sooner.
    float max_dist = 6000;      // this is a very long range, but it works. If you are afraid this causes lag, set it lower.
    double max_v = 500;        // This is used on a hit, to guess wether a target is the same target you were tracking.
                                // I set it to 120, because it seems possible to go over the 100 m/s limit.
    double max_acc = 19.6;  // This number is the maximum acceleration a ship is expected to have. Any higher than this
                            // and the lock may be lost when making a quick velocity change in the transverse direction.
    int max_loop = 100;
    float max_beam_width = 1000;
    int interval = 2;  //Run the script every (n) ticks
    
    string RCName = "Remote Control";
    string RoidPnlName = "Asteroid Panel";
    string GridPnlName = "Grid Panel";
    string TargetPnlName = "Target Panel";
    string TimerName = "Radar Timer";
    string CockpitName = "Cockpit";
    
    IMyRemoteControl rctrl = null;
    IMyTextPanel asteroid_panel = null;
    IMyTextPanel grid_panel = null;
    IMyTextPanel target_panel = null;
    IMyTimerBlock timer = null;
    IMyCockpit cockpit = null;
    
    //Initialize variables
    List<Target> targets = new List<Target>();
    string[] target_strings = null;
    
    int tick = 0;
    bool init = false;
    int GFD_tick = 0;
    
    void Main(string argument)
    {
    	if (argument == "reset")
    		Storage = "";
    	
    	if (!init) {
    		var gts = GridTerminalSystem;
    		rctrl = (IMyRemoteControl)gts.GetBlockWithName(RCName);
    		asteroid_panel = (IMyTextPanel)gts.GetBlockWithName(RoidPnlName);
    		grid_panel = (IMyTextPanel)gts.GetBlockWithName(GridPnlName);
    		target_panel = (IMyTextPanel)gts.GetBlockWithName(TargetPnlName);
    		timer = (IMyTimerBlock)gts.GetBlockWithName(TimerName);
    		cockpit = (IMyCockpit)gts.GetBlockWithName(CockpitName);
    		init = true;
    	}
    	
    	
    	Vector3D rc_pos = rctrl.GetPosition();
    	Vector3D center = rc_pos;
    	
    	// we use the orientation vectors below to print the target arrows on the target LCD
    	Matrix or, world_or;
    	cockpit.Orientation.GetMatrix(out or);
    	world_or = cockpit.WorldMatrix.GetOrientation();
    	
    	Vector3D c_fwd  = Vector3D.Transform(or.Forward, MatrixD.Transpose(or));
    	Vector3D c_up  = Vector3D.Transform(or.Up, MatrixD.Transpose(or));
    	Vector3D c_right  = Vector3D.Transform(or.Right, MatrixD.Transpose(or));
    	
    	Vector3D fwd_wrld = Vector3D.Transform(c_fwd, world_or);
    	Vector3D up_wrld  = Vector3D.Transform(c_up, world_or);
    	Vector3D right_wrld  = Vector3D.Transform(c_right, world_or);
    	
    	// Start Main GFD Routine
    	if (tick % interval == 0) {
    		
    		GFD_tick = 0;
    		targets.Clear();
    		target_strings = LoadStorage();
    		char[] delim = {';'};
    
    		double radius;	
    		for (int i = 0; i < target_strings.Length; i++) {
    			string[] tar = target_strings[i].Split(delim);
    			targets.Add(new Target{time = int.Parse(tar[0])+1, countdown = int.Parse(tar[1])-1,
    			            	radius = double.Parse(tar[2]), velocity = GetVector3D(tar[3]), coords = GetVector3D(tar[4]),
    			            	stat = bool.Parse(tar[5]), grid = bool.Parse(tar[6])});
    		}
    		
    		
    		// Re-scan targets if more than XXX seconds have elapsed since last check
    		for (int i = targets.Count-1; i > -1; i--) {
    			if (targets[i].countdown < 1 && GFD_tick < max_GFD) { //we check if one of the tracked targets needs updating
    				Vector3D target = targets[i].coords + targets[i].velocity * targets[i].time - rc_pos;
    				// we use the velocity to predict where the target has travelled across the sky.
    				target.Normalize();
    				center = Lock3GFD(rctrl,rc_pos+target, max_dist, 0f, out radius);
    				if (!center.IsValid()) { // if something went wrong and we get a bad result,
    					// we count it as 3 GFD calls and remove the target from our list.
    					GFD_tick += 3;
    					targets.RemoveAt(i);
    				}
    				else {
    					if (center == rc_pos) GFD_tick += 1; //the Lock3GFD method returns after 1 GFD call if it misses
    					else {
    						GFD_tick += 3;  //if Lock3GF hits, we count it a 3 GFD calls
    						update_targetlist (targets,  rc_pos, center, radius);  // we only update when something is hit.
    					}
    					if (Math.Abs(radius- targets[i].radius) > 0.1 || (center-rc_pos).Length() < 1) targets.RemoveAt(i);
    					// when the target is too close to be right or its radius is not correct, we remove the target.
    				}
    			}
    		}
    		
    		Random rnd = new Random();
    		
    		//The while loop below shoots GFD beams into random directions to find new targets.
    		//It uses spherical coördinates.
    		
    		int loop_count = 0;
    		
    		while (GFD_tick < max_GFD-2 && loop_count < max_loop) { // we loop over the allowed number of GFD
    			loop_count++;                            // we take a maximum of 100 loops to prevent problems with the script
    			double theta = rnd.NextDouble() * 2 * Math.PI;  // theta is between 0 and 2 pi
    			double z = 2* rnd.NextDouble() -1;        // z is between -1 and 1
    			double z_fac = Math.Sqrt(1- Sq(z)); //the squareroot of 1 minus z-square
    			double x = z_fac * Math.Cos(theta);
    			double y = z_fac * Math.Sin(theta);
    			
    			Vector3D rel_target = new Vector3D(x,y,z); //spherical coordinates transformed to cartesian.
    			
    			float beam_width = max_beam_width;
    			for(int i = 0; i < targets.Count; i++) { //this checks how wide the beam can be without crossing known targets
    				Vector3D target_dir = targets[i].coords - rc_pos;
    				float target_beam_width = (float)(rel_target.Cross(target_dir)).Length();
    				target_beam_width -=(float)targets[i].radius - 1;
    				if (target_beam_width < beam_width) beam_width = target_beam_width;
    				if (target_beam_width < 0) break;
    			}
    			
    			if (beam_width > 0) { // if the beam_width is negative it will hit a known target even with beam_width 0;
    				Vector3D target = rc_pos + rel_target; // we don't need to normalize because the length of rel_target = 1.
    				center = Lock3GFD(rctrl, target, max_dist, beam_width, out radius);
    				
    				if (center == rc_pos) GFD_tick += 1;  //the Lock3GFD method returns after 1 GFD call if it misses
    				else {
    					GFD_tick += 3; //if Lock3GF hits, we count it a 3 GFD calls
    					update_targetlist (targets,  rc_pos, center, radius); // we only update when something is hit.
    				}
    			}
    		}
    	}
    	
    	//Write output to screens
    	if (tick % 4*interval == 0) {
    		
    		// the three panels titles are written below.
    		string gridpnltext = "Grids \nRadius - Coördinates - Speed - Distance  \n\n";
    		string roidpnltext = "Asteroids  \nRadius - Coördinates - Distance  \n\n";
    		string targpnltext = "Total targets: " + targets.Count.ToString() + " Aiming at:\n\n";		
    		
    		// create the target information on the LCD screens.
    		for(int i = 0; i < targets.Count; i++) {
    			Vector3D coords = targets[i].coords;
    			if (!targets[i].stat) {
    				gridpnltext = gridpnltext + (Math.Round(targets[i].radius/1.732,1).ToString()+"m  "+
    				                          Math.Round(coords.GetDim(0),0)+":"+Math.Round(coords.GetDim(1),0)+":"+Math.Round(coords.GetDim(2),0)+
    				                          "  "+Math.Round(60*targets[i].velocity.Length(),1).ToString()+"m/s  "+
    				                          (Math.Round((targets[i].coords - rc_pos).Length(),0)).ToString()+"m\n");
    			}
    			else {
    				roidpnltext = roidpnltext + (Math.Round(targets[i].radius/1.732,1).ToString()+"m  "+
    				                              Math.Round(coords.GetDim(0),0)+":"+Math.Round(coords.GetDim(1),0)+":"+Math.Round(coords.GetDim(2),0)+
    				                              "  "+(Math.Round((targets[i].coords - rc_pos).Length(),0)).ToString()+"m\n");
    			}
    		}
    		
    		grid_panel.WritePublicText(gridpnltext);
    		asteroid_panel.WritePublicText(roidpnltext);
    		target_panel.WritePublicText(targpnltext);
    		
    		double dotprod = -1;
    		int target_index = -1;
    		
    		// cycle through targets and find the one closest to your target reticule with dotproducts.
    		for(int i = 0; i < targets.Count; i++) {
    			Vector3D target_vec = Vector3D.Normalize(targets[i].coords - rc_pos);
    			double dotprod_target =  fwd_wrld.Dot(target_vec);
    			if (dotprod_target > dotprod) {
    				dotprod = dotprod_target;
    				target_index = i;
    			}
    		}
    		
    		// The string stuff below fills the target panel.
    		if (target_index > -1) {
    			char c = '\\';
    			int i = target_index;
    			Vector3D coords = targets[i].coords;
    			Vector3D target_vec = Vector3D.Normalize(targets[i].coords - rc_pos);
    			if (targets[i].stat) target_panel.WritePublicText("Target Type: Asteroid\n", true);
    			else  target_panel.WritePublicText("Target Type: Grid\n", true);
    			
    			target_panel.WritePublicText("Radius: " + Math.Round(targets[i].radius/1.732,1).ToString()+" m\n"+
    			                            "Distance: "+ (Math.Round((coords - rc_pos).Length(),0))+" m\n" +
    			                            "Speed: " + Math.Round(60*targets[i].velocity.Length(),1).ToString() + " m/s\n" +
    			                            "Coördinates: " + Math.Round(coords.GetDim(0),0)+":"+Math.Round(coords.GetDim(1),0)+":"+
    			                            Math.Round(coords.GetDim(2),0)+ "\n\n", true);
    			
    			// the part below creates right/left and up/down arrows pointing in the direction of the projection of
    			//the target on your cockpit view. You can use this to find out which target you are getting information on.
    			
    			if (right_wrld.Dot(target_vec) > 0) target_panel.WritePublicText("              --->\n\n", true);
    			else target_panel.WritePublicText("              <---\n\n", true);
    			
    			if (up_wrld.Dot(target_vec) > 0) {
    				target_panel.WritePublicText("          /" + c +"\n"+ "          |" +"\n" + "          |", true);
    			}
    			else target_panel.WritePublicText("          |" +"\n"+ "          |" +"\n" + "          "+c+"/", true);
    		}
    	}
    	
    	//Update Storage
    	if (tick % interval == 0) {
    		Storage = "";
    		StringBuilder builder = new StringBuilder();
    		
    		for(int j = 0; j < targets.Count; j++) {
    			builder.Append(targets[j].ToString());
    			if (j < targets.Count - 1) builder.Append("\n");
    		}
    
    		Storage = builder.ToString();
    	}
    	
    	timer.GetActionWithName("TriggerNow").Apply(timer);
    	tick ++;
    	if (tick > 10*interval)
    		tick = 0;
    }
    
    Vector3D GetVector3D(string rString){  // convert String to Vector3D
    	if (rString == "0") rString = "(X:0 Y:0 Z:0)";
    	string[] temp = rString.Substring(1,rString.Length-2).Split(' ');
    	double x = double.Parse(temp[0].Substring(2,temp[0].Length-2));
    	double y = double.Parse(temp[1].Substring(2,temp[1].Length-2));
    	double z = double.Parse(temp[2].Substring(2,temp[2].Length-2));
    	Vector3D rValue = new Vector3D(x,y,z);
    	return rValue;
    }
    
    Vector3D Lock3GFD (IMyRemoteControl rctrl, Vector3D target, float max_dist, float beam_width, out double radius) {
    	// this method is commented on the Keen forums except the radius part at the bottom.
    	// I did not come up with this myself, exept the radius calculation. That part is mine.
    	Vector3D R = rctrl.GetPosition();
    	Vector3D F1 = rctrl.GetFreeDestination(target, max_dist, beam_width);  // F,O and R refer to PennyWise's pictures
    	Vector3D RF1 = F1-R;
    	if (RF1.Length() < 1) {
    		radius = 0;
    		return R;
    	}
    	Vector3D RO_norm = Vector3D.Normalize(target-R);
    	Vector3D O1 = R + (F1-R).Length()*RO_norm;
    	Vector3D dO = Vector3D.Normalize(F1-O1);  //named differently in Pennywise's pictures
    	Vector3D O2 = O1 - dO;  // shift the points to the inside of the sphere instead of the outside to avoid miss
    	Vector3D O3 = O2 - dO;
    	Vector3D F2 = rctrl.GetFreeDestination(O2, max_dist, beam_width);
    	Vector3D F3 = rctrl.GetFreeDestination(O3, max_dist, beam_width);
    	Vector3D circle_norm = Vector3D.Cross(F1-O1,O1-R);
    	Vector3D F12 = (F1+F2)/2;
    	Vector3D F23 = (F2+F3)/2;
    	
    	Vector3D FC12 = circle_norm.Cross(F2-F1);
    	Vector3D FC23 = circle_norm.Cross(F3-F2);
    	Vector3D T = Intersect(F12,FC12, F23,FC23);
    	Vector3D RT = T-R;
    	Vector3D RT_RF1 = RT + RF1;
    	
    	// getting line parameter "k" to find O along the line through R and O. We use that FO and TO are perpendicular.
    	
    	double k = (RT_RF1.Dot(RO_norm) - Math.Sqrt(Sq(RT_RF1.Dot(RO_norm))-4*RT.Dot(RF1)))/2;
    	radius = (T- R - k * RO_norm).Length() - beam_width;
    	if (double.IsNaN(radius)) T = R;
    	
    	return T;
    }
    
    Vector3D Intersect(Vector3D Pos1, Vector3D Vec1, Vector3D Pos2, Vector3D Vec2)
    {
    	// this method intersects two vectors that start at pos 1 and pos 2 and point in the direction of vec1 and vec2
    	Vec1.Normalize();
    	Vec2.Normalize();
    	double D11, D22, D12, DP1, DP2, C12, S1, S2;
    	
    	Vector3D Pos_change = Pos2 - Pos1;
    	Vector3D Cross12 = Vec1.Cross(Vec2);
    	
    	D11 = Vec1.LengthSquared();
    	D22= Vec2.LengthSquared();
    	D12 = Vec1.Dot(Vec2);
    	DP1= Pos_change.Dot(Vec1);
    	DP2= Pos_change.Dot(Vec2);
    	C12 = Cross12.LengthSquared();
    	
    	S1= (D22*DP1 - DP2*D12) / C12;
    	S2= (D12*DP1 - DP2*D11) / C12;
    	
    	Vector3D P1 = Pos1 + S1 * Vec1;
    	Vector3D P2 = Pos2 + S2 *Vec2;
    	
    	return (P1+P2)/2;
    }
    
    double Sq(double number) {              // just for squaring stuff.
    	double squared = number*number;
    	return squared;
    }
    
    string[] LoadStorage()
    {
    	char[] delim = {'\n'};
    	string[] data= Storage.Split(delim);
    	if (Storage == "") return new string[0];
    	else return data;
    }
    
    public class Target { // this class describes a datapoint (target) with all the information that goes with it.
    	public int time;    // time since last scan (ticks)
    	public int countdown; // countdown to next scan
    	public double radius; // radius of the target, used for identification
    	public Vector3D velocity; // velocity of the target
    	public Vector3D coords;  // the GPS coords of the target
    	public bool stat; // this is true when a target has velocity 0 and is of a radius that an asteroid may have
    	public bool grid; // this is true when a "supposed" asteroid has ever gotten a velocity.
    	
    	public override string ToString()
    	{
    		return time + ";" + countdown + ";" + radius + ";" + velocity + ";"+ coords + ";" + stat + ";" + grid;
    	}
    }
    
    List<Target> update_targetlist (List<Target> targets,  Vector3D rc_pos, Vector3D center, double radius)
    {
    	if (center.IsValid() && (center - rc_pos).Length() > 1) {
    		bool new_target = true;    // if this is true we have a new target, it may be made false below.
    		for (int i = 0; i<targets.Count; i++) {
    			Vector3D offset = center - targets[i].coords; // we find out if the target is close to another known target
    			
    			// the complicated "if" below checks two situations. If the target is a grid, it checks if a target with the
    			// same radius exists at a location within a sphere of radius "possible distance travelled since last check"
    			
    			// if the target is an asteroid it simply checks if the radius is correct and it has the same location
    			
    			if ((!targets[i].stat && Math.Abs(radius - targets[i].radius) < 0.1 &&
    			    offset.Length() < Math.Max(targets[i].time,1) * max_v/60) || (targets[i].stat &&  //targets[i].time is given in ticks
    			                                                                  Math.Abs(radius - targets[i].radius) < 0.2 && offset.Length() < 1)) {
    				
    				new_target = false;
    				if (targets[i].time > 0) {
    					targets[i].radius = radius;
    					targets[i].velocity = (center-targets[i].coords)/targets[i].time;
    					
    					if ((targets[i].velocity).Length() > 1) { // if a target has ever moved, it is not considered an asteroid
    						targets[i].grid = true;
    						targets[i].stat = false;
    					}
    					
    					if (targets[i].stat) targets[i].countdown = asteroid_check;
    					else targets[i].countdown = (int)Math.Floor(Math.Sqrt(radius/max_acc)*60);
    					
    					targets[i].time = 0;
    					targets[i].coords = center;
    					double real_radius = radius/Math.Sqrt(3);
    					
    					bool asteroid = false;
    					if (targets[i].grid == false) {
    						for (int j = 0; j < 7; j++) {
    							if (Math.Abs(real_radius - 8* Math.Pow(2,j)) < 0.2 && (targets[i].velocity).Length() < 1) {
    								targets[i].stat = true;
    								// this checks whether the radius is the same as a possible asteroids radius and if it moves.
    								// if so, then it is considered a stationary target (possibly asteroid)
    								// if it ever does move, this check will never be made again. Asteroids never move.
    							}
    							
    						}
    					}
    				}
    			}
    		}
    		
    		if (new_target) {
    			targets.Add(new Target{time = 0, countdown = 1, radius = radius,  velocity = new Vector3D(0,0,0), coords = center,
    			            	stat = false, grid = false});
    		}
    	}
    	return targets;
    }
     
  11. Innoble Apprentice Engineer

    Messages:
    238
    That's cool. I bet that helps especially when there are multiple PBs on the server using a script like that. Only problem is that you need to be a bit lucky they don't all call GFD on the same frame.
     
  12. Pennywise Apprentice Engineer

    Messages:
    338
    I think, interval must be taken in account when calculating target velocity and target update rate too.
    --- Automerge ---
    Here is my modification of Innoble's scanner:


    It has "slowdown" option too, but it also can perform "sectoral scan". This allows you to trade "width for depth" and scan planets from huge distance.
     
  13. Flix Trainee Engineer

    Messages:
    4
    Will there be a repair to your script program with the API function of GetFreeDestination being removed?
     
  14. Innoble Apprentice Engineer

    Messages:
    238
    Sorry for the late reply, took a break from space engineers.

    With GFD gone, there is no real replacement possible. This was never going to be the end solution to long range sensing and was considered an exploit. I was actually looking on this forum if there's anything else that has come out for the vanilla game that acts as a LR sensor. There are plenty of mods, for example my Holo projector mod has the ability to act as a long range sensor (and also display it in a 3D projection or on LCD). I would prefer if they added something to the base game as I personally don't like using mods.
     
  15. SpecFrigateBLK3 Senior Engineer

    Messages:
    1,133
    What about raycasting?
     
  16. Innoble Apprentice Engineer

    Messages:
    238
    I am working on a script like that (an improvement over Elfi Wolf's version, which I used as a starting point). It will be slower to map and also track a less reliably, but it will have the option of taking pictures of targets like asteroids, bases and non-moving ships and displaying them on LCD. I hope to have a ready version to share in a week.
     
  17. SpecFrigateBLK3 Senior Engineer

    Messages:
    1,133
    Nice!
    --- Automerge ---
    Slower may help script lag, aye?
     
  18. Innoble Apprentice Engineer

    Messages:
    238
    Well, the fact that it is not GFD helps script lag. It is just slower because while GFD was also a form of Raycast, the GFD beam had a configurable "width" (actually a bit more complicated than that). The "width" made it so that you could effectively make really fat beams that easily hit stuff. The camera raycasts are beams with "width" 0, so their chance to hit is completely dependent on the target. Since space is mostly empty, most beams will not hit anything.

    I try to keep the mapping down to 1 beam per tick (= still 3600 beams a minute!) to conserve resources in MP, but there is no reason you can't up that to 10 beams/tick in a single player game. I made a 750 camera small ship that can map really quickly. It finds many asteroids in a relatively short time. It will scan forever if you let it and become recursively more accurate, the longer it is running. The reason you need that many camera's is that camera's can only beam once every second at 2000 km range. So to do 60 beams a second (= 1x per tick) you would need 60 camera's. The reason I use 750 is because I also want to be able to map at 10 or 20 km range fairly quickly. This doesn't increase script lag, but only the lag that's associated with having that many terminal blocks on a ship (same as having 750 timer gyro's or similar)[​IMG]
    --- Automerge ---
    btw, having trouble inserting steam screenshots, but here is a pic of the imaging part of my script. They are two pictures of a donut asteroid. The left one is a picture where the color tells you the type of target (yellow for asteroid, red for enemy, green for own stuff etc.). the right picture is an attempt to give a sense of "depth.

    http://steamcommunity.com/sharedfiles/filedetails/?id=1080827367
    --- Automerge ---
    Here's another depth pic of the crashed red ship:

    http://steamcommunity.com/sharedfiles/filedetails/?id=1080838034
     
  19. SpecFrigateBLK3 Senior Engineer

    Messages:
    1,133
    Oh. Oh my. That is beautiful.
    Is there a range limiter built into the script, or is that even applicable?
     
  20. Innoble Apprentice Engineer

    Messages:
    238
    The is no range limit, but the way the camera raycast works, long range mapping will be slower. It will be slower for 2 reasons. The first reason is that long range space is bigger. A sphere with twice the radius has 4 times the surface area. With twice the range you are scanning 4 times as much space (think of the surface of a sphere, not the volume).

    The second reason is that the camera has a "charge" that builds up while unused. Longer range raycasts use more of this charge. If you raycast twice the range, it will take twice the charge, so you will effectively be raycasting half as much.

    Also, when i say something takes longer, I mean it takes longer to achieve the same resolution. You can scan at 50, or even 100 km pretty fast, but you won't be catching anything smaller than 500m or so any time soon. That will take days.

    A 100 km scan takes 1000 times as long as a 10 km scan if you want to be able detect the same size object (say... a 30 m ship). It takes 10x longer because of the charge-costs being 10x greater and it takes 100x longer because of the surface area of the search-sphere being 100x larger. 10 x 100 makes 1000x. With my algorithm, you don't have to decide beforehand how accurate you want the scan to be, but you do need to specify the range. The longer you have it turned on, the more accurate the map becomes. There is no limit to this.

    In practice, with a cameraship able to send 1 beam/tick (so 60+ cameras) you will detect many asteroids in a matter of minutes in a densely packed asteroid map. If it is a low density map, you will probably need an hour to detect all asteroids you can see.
     
  21. SpecFrigateBLK3 Senior Engineer

    Messages:
    1,133
    Ah gotcha. The range is specified. When I get the chance to play I'll likely be using a minimalist approach at first, which in this situation would mean a relatively small specified range.
     
Thread Status:
This last post in this thread was made more than 31 days old.