Jump to content

Wanted: C# Camera code


Recommended Posts

Would anyone happen to have a chunk of code lying about that frees the rotation of the camera when a vessel is splashed. Normally, the deeper you go underwater, the more vertical the camera becomes. I'm looking for a way to remove that.

Thanks.

Link to comment
Share on other sites

Ok, apparently no one has any such code lying around. So... I, a complete C# ijut who does program in Pascal and PHP, decided to attempt this myself. And, I have partially succeeded. I found the source for the old Hooligan Labs submarines, which is under the MIT license so it's fully reusable. It contained code to take control of the camera and I have bastardized it and have it mostly working.

Here's the issues I'm having and I hope someone can help me with this.

Here's how it basically works, from what I understand:

If the altitude of the vessel is < 5 meters it either creates a new camera or an instance of a camera, attaches it to the active vessel, and takes control of that camera, removing control of the active flight camera. And it works.

If the altitude > 5 meters, it restores the active flight camera. And that works.

Here's the issues I'm having:

1: When the cameras switch (when the vessel goes above or below the 5m mark), they don't 'remember' the view of the previous camera. I.E., the camera will often instantly rotate 180 degrees. I assume I need to store the camera position when I switch and then assign that position to the one I'm switching to, but no idea how that's done.

2: I'd really like to be able to designate which part the camera is assigned to via the part's config. I.E.

MODULE

{

name=underwatercamera

}

As it stands now, the code doesn't care what vessel is above or below 5m, it could be a splashed stock aircraft, the camera will switch and take control (which is rather rude). I don't want the camera to be replaced if it's not a part with the specific module in the config... and I have no earthly clue how to do that either.

I'd also like to be able, in a separate config, to pre-load and change settings, like the 5m mark where the cameras switch and again, I have no clue how to do that.

Here's the code I have so far:

This is run in the FixedUpdate:


if (FlightGlobals.ActiveVessel.altitude <= 5.0)
{
try
{
if (UnderwaterCamera.ManualControl && (CameraManager.Instance.currentCameraMode == CameraManager.CameraMode.IVA || CameraManager.Instance.currentCameraMode == CameraManager.CameraMode.Internal)) // Exit when IVA too
{
UnderwaterCamera.RestoreCameraParent();
return;
}
}
catch (Exception ex)
{
Debug.Log("[Maritime Pack]RestoreCameraParent Exception!");
return;
}
if (!UnderwaterCamera.ManualControl)
{
try
{
UnderwaterCamera.SetCameraParent();
Debug.Log("[Maritime Pack]Setting Camera Parent");

}
catch (Exception ex)
{
print("[Maritime Pack]Set Camera Exception!"); print(ex.Message);
}
try
{
UnderwaterCamera.ManualControl = true;
cameraManualControl();
Debug.Log("[Maritime Pack]Camera Manual Control Activated");
}
catch (Exception ex)
{
print("[Maritime Pack]Camera Manual Control Exception!"); print(ex.Message);
}
}
else
{
cameraManualControl();
}
}
else
{
if (UnderwaterCamera.ManualControl)
{
Debug.Log("[Maritime Pack]Vessel Above 5.0M");
try
{
UnderwaterCamera.RestoreCameraParent();
}
catch (Exception ex)
{
Debug.Log("[Maritime Pack]Restore Camera Parent Exception!"); print(ex.Message);
}
}
}

And this is the rest of the code.


private void cameraManualControl()
{
if (!UnderwaterCamera.ManualControl)
return;
Debug.Log("[Maritime Pack]Manually Controlling");
_cameraX = 0;
_cameraY = 0;

if (Input.GetMouseButton(1)) // RMB
{
Debug.Log("[Maritime Pack]RMB Clicked");
_cameraX = Input.GetAxis("Mouse X") * UnderwaterCamera.CameraSpeed; // Horizontal
_cameraY = Input.GetAxis("Mouse Y") * UnderwaterCamera.CameraSpeed; // Vertical
}

if (GameSettings.AXIS_MOUSEWHEEL.GetAxis() != 0f) // MMB
{
Debug.Log("[Maritime Pack]Middle Mouse Wheel Scrolled");
CameraDistance =
Mathf.Clamp(
CameraDistance *
(1f - (GameSettings.AXIS_MOUSEWHEEL.GetAxis() * UnderwaterCamera.ActiveFlightCamera.zoomScaleFactor)),
UnderwaterCamera.ActiveFlightCamera.minDistance, UnderwaterCamera.ActiveFlightCamera.maxDistance);
}

UnderwaterCamera.ActiveCameraPivot.transform.RotateAround(UnderwaterCamera.ActiveCameraPivot.transform.position, -1 * FlightGlobals.getGeeForceAtPosition(UnderwaterCamera.ActiveCameraPivot.transform.position).normalized, _cameraX);
UnderwaterCamera.ActiveCameraPivot.transform.RotateAround(UnderwaterCamera.ActiveCameraPivot.transform.position, -1 * UnderwaterCamera.ActiveFlightCamera.transform.right, _cameraY);

UnderwaterCamera.ActiveCameraPivot.transform.position = FlightGlobals.ActiveVessel.transform.position;

UnderwaterCamera.ActiveFlightCamera.transform.LookAt(UnderwaterCamera.ActiveCameraPivot.transform.position, -1 * FlightGlobals.getGeeForceAtPosition(UnderwaterCamera.ActiveFlightCamera.transform.position).normalized);
}

private static double findAltitude(Transform aLocation) //not used at the moment
{
if (FlightGlobals.ActiveVessel == null) return 0;
return Vector3.Distance(aLocation.position, FlightGlobals.ActiveVessel.mainBody.position) - (FlightGlobals.ActiveVessel.mainBody.Radius);
}
}

public static class UnderwaterCamera
{
private static Transform _originalParentTransform;
private static bool _manualControl;

public static FlightCamera ActiveFlightCamera;
public static GameObject ActiveCameraPivot;

public static float CameraSpeed = 0f;
public static float CameraSpeedMulti = 20f;

public static bool ManualControl
{
set
{
if (value && ActiveFlightCamera == null)
{
_manualControl = false;
Debug.Log("[Maritime Pack]Tried to set manual camera control while FlightCamera.fetch was null.");
return;
}
_manualControl = value;
}
get { return _manualControl; }
}

public static void SetCameraParent()
{
// Assign FlightCamera instance to public var.
ActiveFlightCamera = FlightCamera.fetch;

// For replacing the camera when done editing.
if (_originalParentTransform == null)
_originalParentTransform = ActiveFlightCamera.transform.parent;

// For translating the camera
if (ActiveCameraPivot != null) GameObject.Destroy(ActiveCameraPivot);
ActiveCameraPivot = new GameObject("FSCamPivot");
ActiveCameraPivot.transform.position = FlightGlobals.ActiveVessel.transform.position;

ActiveFlightCamera.transform.position = FlightCamera.fetch.transform.position;
ActiveCameraPivot.transform.LookAt(ActiveFlightCamera.transform.position, -1 * FlightGlobals.getGeeForceAtPosition(UnderwaterCamera.ActiveFlightCamera.transform.position).normalized);
ActiveFlightCamera.transform.LookAt(ActiveCameraPivot.transform.position, -1 * FlightGlobals.getGeeForceAtPosition(UnderwaterCamera.ActiveFlightCamera.transform.position).normalized);

// Switch to active object.
ActiveFlightCamera.transform.parent = ActiveCameraPivot.transform;

// Use the FlightCamera sensitivity for the speed.
CameraSpeed = ActiveFlightCamera.orbitSensitivity * CameraSpeedMulti;

// Set the fact that we're controlling the camera manually now.
ManualControl = true;

// Say something.
Debug.Log("[Maritime Pack]FlightCamera switched to: " + FlightGlobals.ActiveVessel.name);
}

public static void RestoreCameraParent()
{
// Restore camera control to vessel.
FlightCamera.fetch.transform.parent = _originalParentTransform;
_originalParentTransform = null;

//Remove the fact that we're controlling the camera.
ManualControl = false;

// Say something.
Debug.Log("[Maritime Pack]FlightCamera restored to vessel.");
}

Thanks for taking a look.

Oh, and obviously if you see any glaing screw-ups in this code, please let me know.

Edited by Fengist
Link to comment
Share on other sites

Well, it appears that I'm asking the wrong questions. It seems everyone who's read this post is either unwilling or unable to answer.

So, I've finally figured out enough code to determine if a MODULE is present in a .cfg. I'm still fighting with the camera flopping around when it switches though.

Link to comment
Share on other sites

The issue is that you are messing around in an area of KSP that hardly anyone has done anything with.

This is only the second time I have even heard of someone trying to manipulate the camera like this, I've never though about looking at it myself.

As for your other questions, there is actually a lack of details needed, notably how is the code running?

You've posted the code for manipulating the camera, but is it on a partModule, a KSPAddon, something else?

D.

Link to comment
Share on other sites

I'm interested in this too. Mainly, I'd like to figure out some way to click on a part and set it as the camera origin point. Looking around large stations is a pain.

So far, the only code I've found is the source for the Camera Tools plugin from KerbalStuff.

The other thing that you can do is run it in a debugger and descend into the KSP assembly, and try to figure things out from there.

Though for the camera, it seems as if it should be possible to just go off from the Unity documentation and examples.

Link to comment
Share on other sites

The issue is that you are messing around in an area of KSP that hardly anyone has done anything with.

This is only the second time I have even heard of someone trying to manipulate the camera like this, I've never though about looking at it myself.

As for your other questions, there is actually a lack of details needed, notably how is the code running?

You've posted the code for manipulating the camera, but is it on a partModule, a KSPAddon, something else?

D.

Yea, I was pretty sure I was jumping into the deep end on this one. I only know of a very few mods that do use code like this, Hooligan being one.

What I eventually figured out (which took 1 line of code) was to create a partmodule in the .cfg and then detect if that partmodule was on the active ship. If so, run the camera code. I tried storing the flightcamera pitch and heading like this:


FCheading = FlightCamera.CamHdg;
FCpitch = FlightCamera.CamPitch;
ActiveFlightCamera = FlightCamera.fetch;

and then assigning the pitch and heading to the activeflightcamera. Of course, that didn't work but spammed the log with errors.

- - - Updated - - -

I'm interested in this too. Mainly, I'd like to figure out some way to click on a part and set it as the camera origin point. Looking around large stations is a pain.

So far, the only code I've found is the source for the Camera Tools plugin from KerbalStuff.

The other thing that you can do is run it in a debugger and descend into the KSP assembly, and try to figure things out from there.

Though for the camera, it seems as if it should be possible to just go off from the Unity documentation and examples.

The code there does work. If you drop below 5m, it does create a camera and when you go back above 5m, it restores the old one. But, it seems to flip the heading 180 degrees and it points the camera at the root part, unlike the normal camera which is COM.

The big problem, while I can kinda follow C#, it's not a language I know, nor do I know the Unity API or the KSP API. Not exactly a great way for a C# total noob to cut his teeth.

Edited by Fengist
Link to comment
Share on other sites

  • 2 weeks later...

Overall it looks promising. Controlling whether or not it happens from a part module is also something I could see working rather well. How I would do it, however, would be to create two modules (technically anyway) where the first would be a part module containing KSPField variables for the varying settings that could be applied to this new camera. That would allow you to define the module in the part.cfg (or whatever it's called for that specific part) and apply any part-specific settings, or add some context menu sliders. This would also make it easier to make the camera override-able from an action group, in case you were using this part on a craft that was exploring a non-atmospheric and/or non-oceanic body with landmasses that dipped more than 5m below what would be called "sea level" for that body. The fact that such things could be detectable in code later is irrelevant here since we want to leave control in the hands of the user when selecting how they want to view their craft. This first module would also be in charge of monitoring the current situation and whether or not the camera should be in underwater mode. Finally, you would need to add a detector for the current body which finds out if the planet has oceans and, if that returns false, to remove the second module from the root part dynamically to eliminate it from taking up system resources constantly checking that the camera is being properly manipulated.

The second part module would be one that is added dynamically to the root part of the vessel, if the body checks out to be oceanic, by the previous part module and will then call forth the settings from its creator module and apply them to the camera, including whether or not to make the switch to or from itself based on whether or not the override action group is active. I might also add the override action group to this module as well, just in case the controller module/part is detached from the ship in flight. This second module will handle all the actual camera work and the maintaining of the camera angles. Looking at the stock KSP code (using something like ILSpy to decompile it into a C# format and doing some digging) you should be able to find out how they calculate the Center of Mass, but I believe KSP probably already provides a method somewhere that would do that for you. If neither of those searches reveal anything, you could always look at the source for other mods that deal with CoM, such as mechjeb or RCS Build Aid (I butchered that name I think). You should then be able to make the camera look at that target with "cameraname.transform.LookAt(vector3 lookat_target)" where "lookat_target" is then the vector3 of the center of mass for the current vessel. In fact, looking at the code just now, I discovered a stock vessel CoM detector in "GetComponent<Vessel>().CoM" which returns a vector3 for the current vessel's center of mass.

It's all quite complicated, but also rather powerful if you just do some digging around in the stock DLLs.

That's just the approach I would take. You could also use a vesselmodule instead of a partmodule for the camera code, but vesselmodule objects are then added to every craft no matter what, so you'd still want some form of controller part module on a part, or added to control parts/command pods/Kerbals (when sitting in a command chair) to set an active/not-active field somewhere.

If you managed to get everything stable enough with the code to auto detect if we are indeed under "water" and not simply below the reference body's "water level" then you might be able to simply eliminate the part module and run it as a vessel module, added to every craft from the get go, and make it more or less seamless. For end-user options you could then either use a GUI like we do in Kerbal Foundries, coupled with a config file in our mod's root directory (and an entire Persistence class that was ingeniously built by *Aqua* (the asterisks are part of his name, not punctuation)), or you could re-add a part module that could optionally override default settings in the vessel module if the part is detected on the craft at startup.

Eventually you'd want to see about making it compatible with any other camera manipulating mods, the easiest of which would be to simply set an override to disable your mod's camera modification if any other camera modifications are currently active, but still monitor the vessel situation so you can re-active when the custom camera is toggled back to the standard camera.

You have been the victim of one of my famous "walls of text." It's an honor, really... if you survive it.

Link to comment
Share on other sites

It sounds like you've made some progress on this. What issues are you still having? With regards to example code for dealing with cameras, you can check out KerbTrack. You can see assigning the heading and pitch of the camera working here. What issues did you run into trying to do the same?

I have made some progress yes. I have code working and working underwater well with one rather big exception. It passes right through the terrain.

I've attempted raycasting downward from the camera and that sorta works and I can get the cameral altitude (which is a negative underwater). I'm pretty sure I can get the heading and pitch but really don't need them. But so far, my attempts to keep it above the terrain... well, the bottle of aspirin beside me is a bit lighter.

Here's the code I'm using to find the terrain, the problem is, in the code above, he's trapping the mouse wheel and the right mouse button and adjusting the camera position when that happens. So far, using either update or fixedupdate, it's not casting the ray faster than the mouse can move so it ends up under the terrain.


if (Physics.Raycast(UnderwaterCamera.ActiveFlightCamera.transform.position, Vector3.down, out hit, 5000))
{
ScreenMessages.PostScreenMessage(new ScreenMessage("Camera Hit: " + hit.transform.name + " Distance: " + hit.distance + " Altitude: " + cameraAltitude + " Y: " + _cameraY, 3f, ScreenMessageStyle.UPPER_LEFT));
}
else
{
hit.distance = 20f;
}


//and before the camera is adjusted... this is my last failed attempt.


if (hit.distance < 20f || cameraAltitude < -998)
_cameraY = -0.1f;

I've also been told that the 'false bottom' of the ocean at 1,000m is a problem but I can get around that. Squad obviously does this with their code but I'm sure it's nothing like the code I have to work with.

Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

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...