Jump to content

The official unoffical "help a fellow plugin developer" thread


Recommended Posts

8 minutes ago, Malah said:

Hello, Is there a way to keep a coroutine active when the game is paused (with something like FlightDriver.setPause(True,True) or in the RnD building) ?

 

Yep, use WaitForSecondsRealtime :)

Link to comment
Share on other sites

18 minutes ago, sarbian said:

Yep, use WaitForSecondsRealtime :)

As easy as this, thanks :wink:

Edit1: I know why I haven't found it ... it seems to be in Unity 5.4 ... I need to wait KSP 1.2 :P

Edit2: After some research, I will use this in KSP 1.1:

		IEnumerator test() {
			int i = 0;
			while (true) {
				Debug.Log ("wait ... " + i);
				i++;
				float pauseEndTime = Time.realtimeSinceStartup + 1;
				while (Time.realtimeSinceStartup < pauseEndTime) {
					yield return 0;
				}
			}
		}

 

Edited by Malah
Link to comment
Share on other sites

19 minutes ago, Malah said:

As easy as this, thanks :wink:

Edit1: I know why I haven't found it ... it seems to be in Unity 5.4 ... I need to wait KSP 1.2 :P

Edit2: After some research, I will use:


		IEnumerator test() {
			int i = 0;
			while (true) {
				Debug.Log ("wait ... " + i);
				i++;
				float pauseEndTime = Time.realtimeSinceStartup + 1;
				while (Time.realtimeSinceStartup < pauseEndTime) {
					yield return 0;
				}
			}
		}

 

Or you can use WaitForSeconds (which exists in 5.2) and multiply by Time.timeScale.

Edited by nightingale
Link to comment
Share on other sites

Ok I didn't want to post in here as the thread name states "help a fellow plugin developer" which I am definitely not. I just really want to know if a plugin is possible for what I have in mind. I am more than willing to do the work and learn, I just need to be pointed in the right direction. I have never made a plugin for ksp but I have a love for coding and programing just not in c# or unity so this is going to be a test run for me. I love to learn so any help and guidance will be greatly appreciated. If you think this wouldn't be worth my time that's cool too. ill definitely be here with an open ear.

HERE  is my proposal. i haven't fully fleshed out the details but I think it has potential. check it out!

Link to comment
Share on other sites

On 8/21/2016 at 6:44 AM, Galileo said:

Ok I didn't want to post in here as the thread name states "help a fellow plugin developer" which I am definitely not. I just really want to know if a plugin is possible for what I have in mind. I am more than willing to do the work and learn, I just need to be pointed in the right direction. I have never made a plugin for ksp but I have a love for coding and programing just not in c# or unity so this is going to be a test run for me. I love to learn so any help and guidance will be greatly appreciated. If you think this wouldn't be worth my time that's cool too. ill definitely be here with an open ear.

HERE  is my proposal. i haven't fully fleshed out the details but I think it has potential. check it out!

Yes, it's possible.

See Multi-tasking Kerbals for a small intro to the trait system (reproduced below since the download link is broken) and making Kerbals do different things.

Also check out Roster Manager which seems to do many of the same things you want to accomplish.

While you can modify the three classes (Pilot, Scientist, Engineer) to have different attributes, I have no idea if you can add additional classes.

 

EXPERIENCE_TRAIT
{
	name = Pilot
	title = Astronaut
	desc = One Kerbal for One Job!

	EFFECT
	{
		name = AutopilotSkill
	}
	
	EFFECT
	{
		name = RepairSkill
	}
	
	EFFECT
	{
		name = VesselScienceReturn
		modifiers = 1.05, 1.1, 1.15, 1.2, 1.25
	}
	EFFECT
	{
		name = PartScienceReturn
		modifiers = 1.05, 1.1, 1.15, 1.2, 1.25
	}
	EFFECT
	{
		name = ScienceSkill
	}
	
}

EXPERIENCE_TRAIT
{
	name = Engineer
	title = Astronaut
	desc = One Kerbal for One Job!

	EFFECT
	{
		name = AutopilotSkill
	}
	
	EFFECT
	{
		name = RepairSkill
	}
	
	EFFECT
	{
		name = VesselScienceReturn
		modifiers = 1.05, 1.1, 1.15, 1.2, 1.25
	}
	EFFECT
	{
		name = PartScienceReturn
		modifiers = 1.05, 1.1, 1.15, 1.2, 1.25
	}
	EFFECT
	{
		name = ScienceSkill
	}
	
}

EXPERIENCE_TRAIT
{
	name = Scientist
	title = Astronaut
	desc = One Kerbal for One Job!

	EFFECT
	{
		name = AutopilotSkill
	}
	
	EFFECT
	{
		name = RepairSkill
	}
	
	EFFECT
	{
		name = VesselScienceReturn
		modifiers = 1.05, 1.1, 1.15, 1.2, 1.25
	}
	EFFECT
	{
		name = PartScienceReturn
		modifiers = 1.05, 1.1, 1.15, 1.2, 1.25
	}
	EFFECT
	{
		name = ScienceSkill
	}
	
}

 

 

Link to comment
Share on other sites

I'd like clarification on a couple of things.

First: Could someone direct me to a flowchart of how the Unity process goes? I get that there's something to do with Start(), Update(), et cetera, but I don't know exactly when all these methods are called.

Second: How does one trigger action groups? E.g. toggling the Gear action group, or trigger the Stage one.

Third: How do I use coroutines?

Link to comment
Share on other sites

28 minutes ago, 0111narwhalz said:

I'd like clarification on a couple of things.

First: Could someone direct me to a flowchart of how the Unity process goes? I get that there's something to do with Start(), Update(), et cetera, but I don't know exactly when all these methods are called.

Second: How does one trigger action groups? E.g. toggling the Gear action group, or trigger the Stage one.

Third: How do I use coroutines?

  1. This topic may shed some light. It's specific to PartModules but a lot of the same stuff applies to other MonoBehaviours 
  2. Something like
    FlightGlobals.ActiveVessel.ActionGroups.ToggleGroup(KSPActionGroup.Gear);

    If you're interested in a specific vessel rather than just the active vessel then you can substitute something there

  3. Have you read the Unity documentation on them? (if yes, then you'll know that it's a bit confusing but I just want to know how much you already know before I try to start explaining things)

Link to comment
Share on other sites

57 minutes ago, blowfish said:

Have you read the Unity documentation on them? (if yes, then you'll know that it's a bit confusing but I just want to know how much you already know before I try to start explaining things)

Many thanks.

I read it, but it's still a little unclear. I'm trying to make a sort of autopilot, so I need it to wait a physics frame between loops (because it makes no sense to change control inputs any more frequently than that). So should I make OnUpdate() call into the coroutine, and then yield at the end of each loop? And then when I call it again, it starts at the line after yield return null;, right?

As for the lifecycle, this is my understanding:

Load() -> OnStart() -> Start() -> OnFixedUpdate() -> FixedUpdate() 
                                                            ^_________________/

Is this representative of reality?

Link to comment
Share on other sites

32 minutes ago, 0111narwhalz said:

Many thanks.

I read it, but it's still a little unclear. I'm trying to make a sort of autopilot, so I need it to wait a physics frame between loops (because it makes no sense to change control inputs any more frequently than that). So should I make OnUpdate() call into the coroutine, and then yield at the end of each loop? And then when I call it again, it starts at the line after yield return null;, right?

That's generally correct.  yield return null will defer the rest of the coroutine to the next frame.  I think this might not be quite what you want however.  If you start the coroutine every frame, then it will run the whole coroutine, just part of it will be a frame after it was started.

47 minutes ago, 0111narwhalz said:

Load() -> OnStart() -> Start() -> OnFixedUpdate() -> FixedUpdate() 
                                                            ^_________________/

Is this representative of reality?

That looks correct for those methods (there are other update methods that are run on a different schedule).

Link to comment
Share on other sites

2 hours ago, RocketSquid said:

How do I set up node_stack_top, node_stack_bottom, and node_attach?

I think this is the wrong thread for this, part configs aren't the same as plugins.  But anyway, the first three numbers are the node's position (relative to the part's origin), and the next 3 are the node's normal (i.e. what direction does it point in).  The last one is optional and defines the size (this must be an integer).  node_attach is special because it defines the actual attach node, stack nodes are anything starting with node_stack_

Link to comment
Share on other sites

9 hours ago, ShotgunNinja said:

I was wondering if somebody know how to extend the maximum zoom allowed in tracking view and map mode.

You could try CameraManager.GetCurrentCamera() and manipulate the near/far clipping panes and the field of view.

Link to comment
Share on other sites

12 hours ago, blowfish said:

That's generally correct.  yield return null will defer the rest of the coroutine to the next frame.  I think this might not be quite what you want however.  If you start the coroutine every frame, then it will run the whole coroutine, just part of it will be a frame after it was started.

How would you suggest I achieve the goal of running a loop once per physframe while keeping a (rather large) set of variables alive between iterations?

Link to comment
Share on other sites

11 minutes ago, 0111narwhalz said:

How would you suggest I achieve the goal of running a loop once per physframe while keeping a (rather large) set of variables alive between iterations?

I'm confused.  Before it sounded like you wanted it to run every other physics frame.  When exactly do you want this to run?

Link to comment
Share on other sites

38 minutes ago, 0111narwhalz said:

How would you suggest I achieve the goal of running a loop once per physframe while keeping a (rather large) set of variables alive between iterations?

Normally I'd go with a wrapper class/struct and a function called from fixed update (function in the class, or just another function for a struct).

I prefer to use coroutines for periodic checks (NOT every frame) or as a sort of psuedo-thread for responding to an event over a finite number of frames. Using them to generate additional infinite loops just fragments code that's running on the same events (ie. I like being able to trace all functions that run every frame back to (fixed) update).

I'd say that's just my personal style guidelines though. Running an infinite loop in a coroutine is not going to have major downsides so if that works for you, why not.

Edited by Crzyrndm
Link to comment
Share on other sites

1 hour ago, blowfish said:

I'm confused.  Before it sounded like you wanted it to run every other physics frame.  When exactly do you want this to run?

Did I? Sorry, I want it to run each physics frame. 1:1 physframe : iteration.

45 minutes ago, Crzyrndm said:

Normally I'd go with a wrapper class/struct and a function called from fixed update (function in the class, or just another function for a struct).

So something like this?

class Autopilot
{
  //Various variables
  public void OnFixedUpdate()
  {
    Iterate(Object);
  }
  void Iterate(Object)
  {
    //Do autopilot-y things
  }
}

Would this preserve the "various variables" between calls of OnFixedUpdate()?

Finally, what's the functional difference between OnFixedUpdate() and FixedUpdate()?

Link to comment
Share on other sites

15 minutes ago, 0111narwhalz said:

Did I? Sorry, I want it to run each physics frame. 1:1 physframe : iteration.

So something like this?


class Autopilot
{
  //Various variables
  public void OnFixedUpdate()
  {
    Iterate(Object);
  }
  void Iterate(Object)
  {
    //Do autopilot-y things
  }
}

Would this preserve the "various variables" between calls of OnFixedUpdate()?

Finally, what's the functional difference between OnFixedUpdate() and FixedUpdate()?

FixedUpdate and OnFixedUpdate are just instance methods, whatever you do to the instance variables will be kept.

OnFixedUpdate on a PartModule is called by the part's FixedUpdate method.  It is only run if the part has been staged.  FixedUpdate is called by Unity on the PartModule (or any other Behaviour).

Link to comment
Share on other sites

10 minutes ago, blowfish said:

FixedUpdate and OnFixedUpdate are just instance methods, whatever you do to the instance variables will be kept.

So I don't need a separate Iterate() method? I probably will anyway, because I need some other methods to run at appropriate times, but it's not necessary?

11 minutes ago, blowfish said:

OnFixedUpdate on a PartModule is called by the part's FixedUpdate method.  It is only run if the part has been staged.  FixedUpdate is called by Unity on the PartModule (or any other Behaviour).

So in this case (partless), I should use FixedUpdate() instead of OnFixedUpdate()?

Link to comment
Share on other sites

Correct, On* methods will not work when inheriting from a module that doesn't implement them for you (including direct from MonoBehaviour (general partless mod)).

and no Fixed update will not store variables for you. You just need to declare them outside the method (loose globals, global wrapper struct instance, global wrapper class instance)

int global var = 0; // increments with every call to Update
class wrapperClass
{
	// wrapper vars
  	int var1;
	// wrapper functions
	void AutoPilotStuff()
	{
		++var1;
	}
}
wrapperClass dataClass = new wrapperClass(); // persistent across method invocations and groups variables nicely

struct wrapperStruct
{
	// wrapper vars
    int var1;
  	// wrapper functions
  	void AutoPilotStuff()
	{
		++var1;
	}
}
wrapperStruct dataStruct = new dataStruct();

void FixedUpdate()
{
  	// not persistent
  	int i = 100; // will be lost when FixedUpdate completes for that frame
  
  	// persistent
	++var; // increase by 1 every physics frame
  	dataStruct.AutoPilotStuff(); // increase by 1 every physics frame
  	dataClass.AutoPilotStuff(); // bundle function and vars for the function together
}

Only variables declared inside the method are lost upon completion of each invocation (If you're new at this, I would avoid using the struct implementation. There are a few gotchas with it)

Edited by Crzyrndm
Link to comment
Share on other sites

So, I'm writing my first mod, and my C# knowledge is.. shaky to say the least, so bear with me.

What I am basically trying to do, is set it so that when a player returns to the Space Centre, if it detects more Dead crew members than it did last time, it takes some rep away from the player. I know I could set up an Event Handler in the flight scene for this, but so far, everything it needs to do happens in the space centre, so I'd like to keep it that way if at all possible.

I know the code works, because when I load up to the Space Centre from the main menu, the rep is deducted properly. However, when returning to the Space Centre, the rep isn't deducted but the number of dead Kerbals is updated (and outputted to the text file where I'm storing this stuff at the moment).

I'm guessing what is happening is that the game is calling the first Update() before it's loaded the crew list, so it's not finding them. What's weird though is the number of dead Kerbals is being updated properly (which I wouldn't expect to happen if it hadn't finished loading, and it would pick it up on the next pass). Thinking this was my problem, I tried using WaitForSeconds to make sure everything had loaded, but no joy.

This is the relevant bits of my code (apologies for the terrible forum formatting) - I've pruned some bits from Budget() which are working fine, and not relevant to the problem:

Quote

void Update()
        {
            Wait();
            Budget();
        }

private void Budget()

{     

IEnumerable<ProtoCrewMember> DeadCrew = HighLogic.CurrentGame.CrewRoster.Crew.Where(k => k.rosterStatus == ProtoCrewMember.RosterStatus.Dead || k.rosterStatus == ProtoCrewMember.RosterStatus.Missing);

        if (DeadCrew.Count() > DeadCount)
                {
                    Reputation.Instance.AddReputation(-100, TransactionReasons.None);
                    DeadCount = DeadCrew.Count();
                }

}

IEnumerator Wait()
        {
            yield return new WaitForSeconds(10);
        }

So I guess I have two questions:

1) What am I doing wrong? Why is it only doing it on a new save load and not a scene change? It's probably a really obvious rookie mistake.

2) Is there a better way of doing this?

 

Edited by severedsolo
Link to comment
Share on other sites

11 minutes ago, severedsolo said:

So, I'm writing my first mod, and my C# knowledge is.. shaky to say the least, so bear with me.

What I am basically trying to do, is set it so that when a player returns to the Space Centre, if it detects more Dead crew members than it did last time, it takes some rep away from the player. I know I could set up an Event Handler in the flight scene for this, but so far, everything it needs to do happens in the space centre, so I'd like to keep it that way if at all possible.

I know the code works, because when I load up to the Space Centre from the main menu, the rep is deducted properly. However, when returning to the Space Centre, the rep isn't deducted but the number of dead Kerbals is updated (and outputted to the text file where I'm storing this stuff at the moment).

I'm guessing what is happening is that the game is calling the first Update() before it's loaded the crew list, so it's not finding them. What's weird though is the number of dead Kerbals is being updated properly (which I wouldn't expect to happen if it hadn't finished loading, and it would pick it up on the next pass). Thinking this was my problem, I tried using WaitForSeconds to make sure everything had loaded, but no joy.

This is the relevant bits of my code (apologies for the terrible forum formatting) - I've pruned some bits from Budget() which are working fine, and not relevant to the problem:

So I guess I have two questions:

1) What am I doing wrong? Why is it only doing it on a new save load and not a scene change? It's probably a really obvious rookie mistake.

2) Is there a better way of doing this?

 

Bit hard to tell with just that snippit.
But you seem to be over-doing it. And I think a GameEvent is exactly what you want.
All you need is a MonoBehaviour class using KSPAddOn to have it start at MainMenu (or SpaceCenter) scene. Tell Unity not to destroy it between scenes.
Register for the GameEvent.onCrewKilled  and then put your code in that method.
so it would look something like this:
 

[KSPAddon(KSPAddon.Startup.SpaceCentre, true)]
public class DeadKerbalPenaliser : MonoBehaviour
{
   public void Awake()
   {
       DontDestroyOnLoad(this);
	   GameEvents.onCrewKilled.Add(onCrewKilled);
   }
    
   public void OnDestroy()
   {
        GameEvents.onCrewKilled.Remove(onCrewKilled);
   }

   public void onCrewKilled(EventData<EventReport> evtdata)
   {
        // Put your code in here.
        // evtdata.sender will contain the Name of the kerbal that just died.
   }
}

   

 

Edited by JPLRepo
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...