Sunday 15 December 2013

UnityPlayer as Subview with Transparent Background (iOS)

In a previous post I was experimenting with Android & Unity to be able to use Unity as subview overlaying a native UI made with Android... now I tried to do the same but with iOS.

Since Unity 4.2 it became easier to override unity ios view to do your own custom one and combine it with native views inside iOS application.

In this post I will outline the steps to have a simple unity scene with native controls using Unity 4.2 (basic license).

Unity Scene
  1. For simplicity I created a scene with a rotating cube and for the camera I set the color to (0,0,0,0) 
  2. Inside one of my "RotatingCube" gameobject's scripts I added a listener function that simply toggle the direction everytime it is called:
  3. void OnTogglePressed(string message)
    {
         Debug.Log("Toggle direction");
         direction *= -1;
    } 
    
  4. Once all done save the scene and build for iOS to generate XCode project


The generated XCode project
  1. First thing to do is to support transparent background in unity view. To do that simply do a minor change to CreateSystemRenderingSurface method inside GlesHelper.mm to have:
    surface->layer.opaque = NO;
  2. Create UIViewController (you can also subclass UnityViewControllerBase if you wish) with some native controls: In my case I simply added a UILabel and UIButton (which I will use to toggle rotation direction) 
  3. Create a subclass for UnityAppController (e.g. MyUnityAppController) where we will override some methods:
  4.  
    // the header file
    #import "UnityView.h"
    #import "UnityAppController.h"
    @interface MyUnityAppController : UnityAppController
    @end
    
    // the implementation file
    #import "MyUnityAppController.h"
    #import "MyNativeViewController.h"
    @interface MyUnityAppController ()
    @end
    @implementation MyUnityAppController
    // I simply removed the user interaction from unityview
    - (UnityView*)initUnityViewImpl
    {
        UnityView *unityView = [[UnityView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        unityView.userInteractionEnabled = NO;
        return unityView;
    }
    // here I am using my UIViewController as _rootController
    - (void)createViewHierarchyImpl
    {
     _rootView = _unityView;
     _rootController = [[MyNativeViewController alloc] init];
    }
    // I had to override this method (although unity doesn't recommend it) to change one line 
    // instead of assigning _rootView as _rootViewController.view I've added it as a subview
    - (void)createViewHierarchy
    {
     [self createViewHierarchyImpl];
     NSAssert(_rootView != nil, @"createViewHierarchyImpl must assign _rootView");
     NSAssert(_rootController != nil, @"createViewHierarchyImpl must assign _rootController");
        
     [_rootController.view addSubview:_rootView];
     if([_rootController isKindOfClass: [UnityViewControllerBase class]])
      [(UnityViewControllerBase*)_rootController assignUnityView:_unityView];
    }
    @end
    
  5. Inside my UIViewController I set the UIButton to call "OnTogglePressed" method mentioned earlier when building the scene using UnitySendMessage method:
  6.  
    -(IBAction)togglePressed
    {
        UnitySendMessage("RotatingCube", "OnTogglePressed", "");
    }
    
  7. Last thing to do is to change the appcontroller name inside main.mm from UnityAppController to MyUnityAppController then you should be able to see the cube scene with the label and the button:
Video of it in action:



There are few things which need to be investigated: for example whether we can have only a part of the screen for Unity Scene (similar to what I've done with Android). Setting unityview frame didn't give me expected results so I might need to dig deeper.
For now this seems to work using full screen.

If anyone have good ideas or did something similar please share! :-)

Monday 9 December 2013

"Infinite Scrolling" for Unity3D Using NGUI's UIScrollView (1st attempt)

For latest update please check this post instead
I've been using NGUI for UI related work on my Unity project for the past few weeks and it is a breath of fresh air when compared to the stock UnityGUI (OnGUI()).

Part of my project relate to display a relatively large amount of data dynamically (well not very large but in thousands) and instantiating a prefab for each data element didn't seem like a good idea to me :-)

So instead I decided to add some logic to do a scroll view with fixed pool of items that I reuse and position according to the direction of the scroll and get it fed with the correct data.

Although I am sure that there are existing solutions I decided to do my own.

The logic so far is built using UIGrid with fixed cell height for the moment (not well suited for UITable with different cell height) and the panel is using soft clip. the Scroll view is using momentum only (Spring physics breaks my current logic for some reason)

Initialization steps:
  1. Pool size is calculated using the cell height against the panel height plus some buffer (currently I am using a buffer of 4 list items)
  2. List items' pool is instantiated with corresponding data from the data list
  3. A map is maintained to keep track of which data item are used in which list items
While Scrolling:
  1. Any item that turns a visible from an invisible state will notify the main controller
  2. We check the direction of the scroll based on which item is visible (e.g. if the next item is visible means that we are dragging upwards)
  3. based on the direction we reuse the items at the top or the bottom of the list accordingly (e.g. if direction is up the top item moves to the bottom and get populated with data from the corresponding data element)

This is a first attempt and further posts will follow as the logic evolve.

==UPDATE==
I am in the process of making this component available as open source.
Meanwhile we've launched a free app on Android that uses it called Avatar Messenger
==UPDATE 2==
 The component is available as open source
https://github.com/yaseralhaidery/InfiniteList_NGUI

Monday 5 August 2013

Android Maven with Unity3D & JNI

In my previous post I wrote about my experiment in adding Unity view as subview inside an existing simple android project... which worked well until I tried to do the same with a giant android project that utilises maven and uses multiple libraries.

As an iOS developer that was a new experience to me. And, to be honest, although I understand the idea behind maven I felt it was too much hustle for what it is and more over when it comes to android support in eclipse there is a long way to go to have easy to use solution. (I ended up using the command line instead of eclipse)

Anyway, after some trial and error (and a headache) I managed to have something that kind of work. I will share it here if it helps anyone who is trying to do something similar & also for my future reference :-) (using Unity 4.2 and a Mac OS X)

In this post we will refer to unity android project as generated project and the project we are trying to integrate into as targeted project.

Please note that what this post might work for one man band only, especially when it comes to maven related steps as I'm no expert in it (this is my first encounter with maven) and I did it just for experimenting with Unity:

1. Export your unity project as android project

I usually put the generated project in the same directory as the targeted project.

If you are using eclipse don't forget to do the steps mentioned in my previous post for generating a jar by ticking "Is Library" in the generated project properties and adding both the classes jar and generated jar to the targeted project properties->Java build path & Android library.

2. "Mavenize" Unity libs

By installing Unity jars and so libs from the generated project into your local maven repository so you can add them as dependency in your pom.xml file.

Starting with jars:

- untiy-classes.jar (You may notice that I spelled the classes jar as "untiy-classes.jar"... it is typo in unity generated project and hopefully they will notice it in future update :-) :
mvn install:install-file -Dfile=/PATH_TO_GENERATED_PROJ/libs/untiy-classes.jar -DgroupId=Unity3D  -DartifactId=Unity3D -Dversion=4.2.0 -Dpackaging=jar

- unityplayerproxyactivity.jar (generated when you tick the generated project as android library)
mvn install:install-file -Dfile=/PATH_TO_GENERATED_PROJ/bin/unityplayerproxyactivity.jar -DgroupId=UnityPlayerProxyActivity  -DartifactId=UnityPlayerProxyActivity -Dversion=1.0.0 -Dpackaging=jar

You can give any names and versions you want... preferably to use the actual numbers related to the engine and your project though.

Android maven plugin documentation mention that you can add also the native libs (so) in similar fashion although it didn't work for me for some reason

Instead I did an ant task to copy the so files (Unity4.2 generated project will have libmain.so libunity.so libmono.so inside libs/armeabi-v7a) to the target folder during the clean phase of maven (if anyone has better idea please shout it and share!) and I also copied the assets folder contents to the targeted project assets folder

Targetted project's pom.xml:
//inside dependencies tag:
<dependency>
 <groupId>Unity3D</groupId>
 <artifactId>Unity3D</artifactId>
 <version>4.2.0</version>
</dependency>
<dependency>
 <groupId>UnityPlayerProxyActivity</groupId>
 <artifactId>UnityPlayerProxyActivity</artifactId>
 <version>1.0.0</version>
</dependency>

// inside build plugins tag include an ant task
// this will copy so files and assets to the targeted project during
// clean phase of maven
<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-antrun-plugin</artifactId>
 <version>1.7</version>
 <executions>
  <execution>
  <id>ant-copy-so-assets</id>
  <phase>clean</phase>
  <configuration>
   <tasks>
    <echo message="***** copy unity assets and libs! *****" />
    <copy
    todir="${basedir}${file.separator}target${file.separator}classes${file.separator}lib${file.separator}armeabi-v7a${file.separator}">
     <fileset dir="PATH_TO_GENERATED_PROJ_SO_FILES" />
    </copy>
    <copy
    todir="${basedir}${file.separator}assets" overwrite="true">
     <fileset dir="PATH_TO_GENERATED_PROJ${file.separator}assets" />
    </copy>
   </tasks>
  </configuration>
  <goals>
   <goal>run</goal>
  </goals>
  </execution>
 </executions>
</plugin>

3. Follow the previous instructions for integrating Unity view (4 or 5)

4. Execute maven command to build to device
mvn clean package android:deploy android:run -Dandroid.device=usb
If everything is done correctly your android project should run in the device with unity view as well

5. Updating unity project
lets say you added new assets or new logic to your game/ view in Unity then you can simply export the new android project into the same location (overwrite) then execute maven command as in step 4 (no need to repeat steps 1- 3)

Well... that's all for now... although its still not a perfect workflow I hope it will help someone

Thursday 18 July 2013

UnityPlayer as a Subview with Transparent Background (in Android)

Note: for iOS you can check this post
 
In the past couple of days I was trying and add 3D scene created with Unity into an existing Android app where most views are made in native android and make the camera background transparent.

After searching I found these posts in Unity forum which were helpful in finding most the info I needed:
http://forum.unity3d.com/threads/98315-Using-Unity-Android-In-a-Sub-View
http://forum.unity3d.com/threads/80405-Android-Augmented-Reality-Question

I tried to put everything together and ended up with the following steps for future reference  (using Unity 4.1.5):

1. Create Android Unity Project

The first step is to create a Unity scene (if you don't have a project already)
in this case I had a simple cube spinning around its Y axis, I also set the camera background color RGBA to 0,0,0,0 (alpha 0).



2. Creating eclipse project from Unity Project

Having not done much of Android native development (most my work is with iOS) it was a little challenging at first to get my head around back into eclipse but thankfully Unity 4's option to export to eclipse saved me a lot of time: simply tick create eclipse project layout in the build settings of then export.
Import the exported project inside eclipse then go to properties->Android and tick "Is Library" check box inside Library section then apply.

3. Create new Android project (or import an existing one) which has at least one view.

Once you have your project do the following 5 steps:

     a. Go to properties-> Android and inside the Library section click Add and select Unity generated project

     b. Go to properties->Java Build Path->Add External JARs... and select classes.jar from inside the generated eclipse project from Unity

     c. Copy the contents of assets folder inside Unity Eclipse project (bin & libs folders) to your targeted Android project assets folder

     d. Modify AndroidManifest.xml by copying the activities and any features from the AndroidManifest.xml that is found inside Unity Eclipse project  (make sure to remove the intent filter for the Main launcher) . The following is an example of how the end result should look like:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    installLocation="auto"
    package="com.example.testnative"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-feature android:glEsVersion="0x00020000" />

    <supports-screens
        android:largeScreens="true"
        android:normalScreens="true"
        android:smallScreens="false"
        android:xlargeScreens="false" />

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />

    <application
        android:debuggable="false"
        android:icon="@drawable/app_icon"
        android:label="@string/app_name" >
        <activity
            android:name="com.example.testnative.FullscreenActivity"
            android:configChanges="orientation|keyboardHidden|screenSize"
            android:label="@string/app_name"
            android:theme="@style/FullscreenTheme" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
   
        <activity
            android:name="com.unity3d.player.UnityPlayerProxyActivity"
            android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen"
            android:label="@string/app_name"
            android:launchMode="singleTask"
            android:screenOrientation="portrait" >
        </activity>
        <activity
            android:name="com.unity3d.player.UnityPlayerActivity"
            android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen"
            android:label="@string/app_name"
            android:launchMode="singleTask"
            android:screenOrientation="portrait" >
        </activity>
        <activity
            android:name="com.unity3d.player.UnityPlayerNativeActivity"
            android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen"
            android:label="@string/app_name"
            android:launchMode="singleTask"
            android:screenOrientation="portrait" >
            <meta-data
                android:name="android.app.lib_name"
                android:value="unity" />
            <meta-data
                android:name="unityplayer.ForwardNativeEventsToDalvik"
                android:value="false" />
        </activity>
    </application>

    <uses-feature android:name="android.hardware.touchscreen" />
    <uses-feature
        android:name="android.hardware.touchscreen.multitouch"
        android:required="false" />
    <uses-feature
        android:name="android.hardware.touchscreen.multitouch.distinct"
        android:required="false" />

    <uses-permission android:name="android.permission.WAKE_LOCK" />

</manifest> 

    e. Inside the main layout file (or any view you want to use) add a FrameLayout and give it an id of your choice (in my case its "UnityView").

4. Basic Usage

With bit of luck now you should have everything setup and you can render your Unity project to the frame layout you've created as follows:
        
// you need to declare m_UnityPlayer as variable of type UnityPlayer obviously... 
// also include any missing references by pressing shift+ctrl+o (or cmd+shift+o for Mac)
 m_UnityPlayer = new UnityPlayer(this);
 int glesMode = m_UnityPlayer.getSettings().getInt("gles_mode", 1);
 m_UnityPlayer.init(glesMode, false);
 FrameLayout layout = (FrameLayout) findViewById(R.id.UnityView);
 LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
 LayoutParams.MATCH_PARENT);
 layout.addView(m_UnityPlayer.getView(), 0, lp);

5. Add Transparent Background (not working in Unity 4.3 and above since Unity changed the implementation for UnityPlayer)

For the transparent background, as mentioned in the second post referenced above you will have to extend UnityPlayer class and implement GLSurfaceView renderer. This wasn't straight forward and it required some trial and error but this worked for me at the end by replacing setZOrderMediaOverlay(true) with setZOrderOnTop(true)
 
    // the class extending unity player
    class CustomUnityPlayer extends UnityPlayer implements GLSurfaceView.Renderer {
      public CustomUnityPlayer(ContextWrapper context) {
          super(context);
      }
      public void onDrawFrame(GL10 gl) {
          super.onDrawFrame(gl);
      }
    }
     
    // inside OnCreate function:
    m_UnityPlayer = new CustomUnityPlayer(this);
    int glesMode = m_UnityPlayer.getSettings().getInt("gles_mode", 1);
    m_UnityPlayer.init(glesMode, false);
    
    //Create GLSurfaceView with transparent background 
    mUnityView = new GLSurfaceView(getApplication());
    mUnityView.setEGLContextClientVersion(2);
    mUnityView.setZOrderOnTop(true);
    mUnityView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
    mUnityView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
    mUnityView.setRenderer(m_UnityPlayer);
     
    FrameLayout layout = (FrameLayout) findViewById(R.id.UnityView);
    LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
    LayoutParams.MATCH_PARENT);
    layout.addView(mUnityView, 0, lp); 

Once you made this step you will notice that UnityPlayer is not receiving Input events such as touch events any more which means that you will need to pass it yourself depending own your requirements. Also I noticed that when using a shader with color such as Diffuse the objects render with semi transparency while it works well when using mobile diffuse so there might be somethings I missed in this approach

This was my first experiment of this kind with Android and Unity & I hope it will help someone... 
My next step is to do the same thing but for iOS once I have the time.

==UPDATE==
If the Android project you are trying to integrate into uses maven I wrote a little post that might be helpful

==UPDATE 2==
I finally was able to post about my attempt with iOS

Sunday 2 June 2013

Days 5 & 6: Updating the Robot

So I was trying to find time to finish things on the game. I decided to polish the robot's look & feel and behavior first.

This time I did more rounded surfaces for the robot and tried to keep the polygons count as low as possible:


I've also included more movement animations for the robot and added some "fake" glow. Also tweaking the flying UFO AI and a draft for health bar.

Haven't decided on the final look and feel for the whole game yet but its getting there..
(The frame rate is bad due to the screen recorder I am using... The game runs on 60):


I will stop doing updates on the game for the time being since I started doing the serious bits and creating the gameplay ;-)


Thursday 30 May 2013

Brand New Robot To Join The Family

Ok so I spent some time to do an upgrade for the Robot in my game.

Being a developer and not a real artist I kept things simple:


and here is the result inside Unity after applying shaders and some effects:


Sunday 26 May 2013

Day 4: mini update to the game

I was busy for the past month with work and some trips and didn't have the time to work on my small side project game.

This time I simply focused on having a better control system. I found a really good guide on Gamasutra regarding this.
Instead of having two static buttons I choose to have a dynamic ones that appear only when the user touch the correspoding side of the screen and its not a surprise that they are already available options in Unity's Asset store to save time and I did use one.

I had to do modifications to make it work as I wanted but still much quicker than starting from scratch (less than 30 minutes to replace the control)




Tuesday 23 April 2013

Day 3 with my shooter game

I had the chance last weekend to spent some time on the game.
I removed the jump button and instead I am using one button for moving and jumping and the other for shooting.


Also did some work on the visuals (excuse my artwork for the buttons :-P) to add some colour and make it look bit polished.



Saturday 13 April 2013

Beware Of The Mini UFO

Today I had some time to catch up with the game and now I have a "playable" prototype version.
I've added one type of enemy AI for now, health , basic waves logic and some scoring to make it look like a serious project :-P
Still long way to go:




Next steps is to optimise this version and do some game play design

Friday 12 April 2013

Light calculations inside Unity's CG shader

Unity provides Built-in values that can be used to do the lighting calculations inside CG shaders.
However, the documentation seems to be outdated because when i tried using the values:
 _ModelLightColor[0], _ObjectSpaceLightPos[0]
I got undefined variable error.

After digging into UnityCG.cginc I found unity_LightPosition[0] &unity_LightColor[0] variables which did the trick.

Here is an example of a basic per-vertex diffuse lighting Shader that accepts a texture and an ambient color inputs and adds one light calculations:
Shader "Custom/Per-Vertex diffuse" {
      Properties {
        _MainTex ("Texture 1", 2D) = "white" {}
        _Ambient("Ambient Color",Color) = (0.3,0.3,0.3,1)
      }
      SubShader {
      Tags { "RenderType"="Opaque"}
      Pass {
           Tags { "LightMode" = "Vertex" }//otherwise no light related values will be filled
           CGPROGRAM
           #pragma vertex vert
           #pragma fragment frag
           #include "UnityCG.cginc"
   
           sampler2D _MainTex;
           fixed4 _Ambient;
           half4 _MainTex_ST;
   
           struct v2f{
               fixed4 position: POSITION;
               half2 uv_MainTex:TEXCOORD0;
               fixed4 color: COLOR;
           };
   
           v2f vert (appdata_base ab) {
               v2f o;
    
               o.position = mul(UNITY_MATRIX_MVP,ab.vertex);
               o.uv_MainTex = TRANSFORM_TEX(ab.texcoord.xy , _MainTex);
    
               // per vertex light calc
               fixed3 lightDirection;
               fixed attenuation;
               // add diffuse
               if(unity_LightPosition[0].w == 0.0)//directional light
               {
                  attenuation = 2;
                  lightDirection = normalize(mul(unity_LightPosition[0],UNITY_MATRIX_IT_MV).xyz);
               }
               else// point or spot light
               {
                  lightDirection = normalize(mul(unity_LightPosition[0],UNITY_MATRIX_IT_MV).xyz - ab.vertex.xyz);
                  attenuation = 1.0/(length(mul(unity_LightPosition[0],UNITY_MATRIX_IT_MV).xyz - ab.vertex.xyz)) * 0.5;
               }
               fixed3 normalDirction = normalize(ab.normal);
               fixed3 diffuseLight =  unity_LightColor[0].xyz * max(dot(normalDirction,lightDirection),0);
               // combine the lights (diffuse + ambient)
               o.color.xyz = diffuseLight * attenuation + _Ambient.xyz;
               return o;
           }
   
           fixed4 frag(v2f i):COLOR{
              fixed4 c = tex2D(_MainTex, i.uv_MainTex);   
              return  c * i.color;
           }
           ENDCG
      }
      }
Fallback "Mobile/Diffuse"
}


To learn about the math of differnt types of lighting I recommend Nvidia's Tutorial for CG shaders

Sunday 7 April 2013

Fill Shader for Unity

I wrote a simple shader that can be used to "fill" a 3d model with a color gradient and texture by feeding a fill input.

The shader has two passes:
  • One pass to draw the border
  • The other uses the color and texture inputs + fill parameter that I check against the UV coordinates and set alpha accordingly

The following is the result for Y-axis UV "fill"



Thursday 28 March 2013

Simple Game Using Unity

Despite the fact I've been developing apps using Unity for a while now, I didn't have the chance to use it for its main purpose yet "Making Games".

So just for fun I started a small project on the side and use my own time to create a simple shooting platformer game using Unity targetting both iOS and Android.

I used the Japanese 3D editor Shade3D for Unity to create a draft model for the player
(Being a developer with very basic modeling skills I kept it simple for now :-P)

  I started by making the player controller and simple shooting logic which is still work in progress:


Monday 25 March 2013

Unity3D and CG/HLSL shaders

I've been using Unity3D to make apps for iOS and Android for more than a year and half and its really powerful game engine with useful tools that make life easier for developers.

I didn't write alot of shaders before using Unity but the documentation for ShaderLab (Unity's cross platform shader solution) was a good start and I started to write my shaders using surface shader.

However, after having some performance issues with one of my apps on iPhone 4 which was running on 20fps I decided to rewrite my per-vertex lighting shaders (even the basic ones) using CG/HLSL and the results were very good as the frame rate jumped to 45fps.

After using Xcode's OpenGL debugger it seems that the generated GLSL vertex and fragment shaders from unity's surface shaders includes alot of not required code as well as light calculations inside the fragment shaders even if the LightMode set to Vertex.

I am not saying that you shouldn't use surface shaders but when performance matters (especially if you are targetting old iPhone/Android hardware) consider switching to writing vertex and fragment shaders directly using CG.

A good guide to start writing in CG/HLSL is Nvidia's CG tutorial which is available online
For lighting calculation inside CG shaders, Unity provide us with built in values and methods that can be used to get light related parameters such as location and color.

Samples and profiler results will follow

Note: I am refering to the generated shaders in Unity 3.5.7 so there might be different results in the latest versions of Unity.
 


Friday 22 March 2013

Calculate Sunrise and Sunset

More than a year ago when I was building Weather Dog which is a 3D weather app for mobile I realized that the weather API provided to me didn't include the times for sunrise and sunset.
After a quick search I found this simple but really useful algorithm which I simply translated to UnityScript (Javascript):
static function CalculateSunRiseSunSet(date: System.DateTime, lat: float, lng: float, offset: float)
{
 // calc the day of the year
 var N1: float = Mathf.Floor(275.0 * date.Month /9.0);
 var N2: float = Mathf.Floor((date.Month +9.0)/12.0);
 var N3: float = 1 + Mathf.Floor((date.Year - 4 * Mathf.Floor(date.Year / 4) + 2) / 3);
 var N = N1 - (N2 * N3) + date.Day - 30;
 
 // convert the longitude to hour value and calculate an approximate time

 var lngHour: float = lng / 15.0;

 var tRise = N + ((6 - lngHour) / 24.0);
 var tSet = N + ((18 - lngHour) / 24.0);
  
 // calculate the Sun's mean anomaly

 var MRise = (0.9856 * tRise) - 3.289;
 var MSet = (0.9856 * tSet) - 3.289;

 // calculate the Sun's true longitude

 var LRise = MRise + (1.916 * Mathf.Sin(Mathf.PI/180.0 *MRise)) + (0.020 * Mathf.Sin(Mathf.PI/180.0 *2 * MRise)) + 282.634;
 var LSet = MSet + (1.916 * Mathf.Sin(Mathf.PI/180.0 *MSet)) + (0.020 * Mathf.Sin(Mathf.PI/180.0 *2 * MSet)) + 282.634;
 
 if(LRise < 0)
 {
  LRise += 360;
 }
 if(LSet < 0)
 {
  LSet += 360;
 }
 if(LRise >= 360)
 {
  LRise -= 360;
 }
 if(LSet >= 360)
 {
  LSet -= 360;
 }
 // calculate the Sun's right ascension

 var RARise = (180.0/Mathf.PI) * Mathf.Atan(0.91764 * Mathf.Tan(Mathf.PI/180.0 *LRise));
 var RASet = (180.0/Mathf.PI) * Mathf.Atan(0.91764 * Mathf.Tan(Mathf.PI/180.0 *LSet));

 if(RARise < 0)
 {
  RARise += 360;
 }
 if(RASet < 0)
 {
  RASet += 360;
 }
 if(RARise >= 360)
 {
  RARise -= 360;
 }
 if(RASet >= 360)
 {
  RASet -= 360;
 }
 
 // right ascension value needs to be in the same quadrant as L

 var LquadrantRise  = (Mathf.Floor( LRise/90.0)) * 90;
 var RAquadrantRise = (Mathf.Floor(RARise/90.0)) * 90;
 RARise = RARise + (LquadrantRise - RAquadrantRise);
 var LquadrantSet  = (Mathf.Floor( LSet/90.0)) * 90;
 var RAquadrantSet = (Mathf.Floor(RASet/90.0)) * 90;
 RASet = RASet + (LquadrantSet - RAquadrantSet);
 
 //right ascension value needs to be converted into hours

 RARise = RARise / 15.0;
 RASet = RASet / 15.0;
 
 //calculate the Sun's declination

 var sinDecRise = 0.39782 * Mathf.Sin(Mathf.PI/180.0 *LRise);
 var cosDecRise = Mathf.Cos(Mathf.PI/180.0 *(180.0/Mathf.PI) *Mathf.Asin(sinDecRise));
 var sinDecSet = 0.39782 * Mathf.Sin(Mathf.PI/180.0 *LSet);
 var cosDecSet = Mathf.Cos(Mathf.PI/180.0 *(180.0/Mathf.PI) *Mathf.Asin(sinDecSet));
 
 //calculate the Sun's local hour angle
 var cosHRise = (Mathf.Cos(Mathf.PI/180.0 *90.5) - (sinDecRise * Mathf.Sin(Mathf.PI/180.0 *lat))) / (cosDecRise * Mathf.Cos(Mathf.PI/180.0 *lat));
 var cosHSet = (Mathf.Cos(Mathf.PI/180.0 *90.5) - (sinDecSet * Mathf.Sin(Mathf.PI/180.0 *lat))) / (cosDecSet * Mathf.Cos(Mathf.PI/180.0 *lat));
 if (cosHRise >  1)
  return;
 if (cosHSet < -1)
  return;
  
 // finish calculating H and convert into hours

 var HRise =(360 - ( (180.0/Mathf.PI) *Mathf.Acos(cosHRise)));
 var HSet = (180.0/Mathf.PI) *Mathf.Acos(cosHSet);
 HRise = HRise / 15.0;
 HSet = HSet / 15.0;
  
 //calculate local mean time of rising/setting

 var TRise = HRise + RARise - (0.06571 * tRise) - 6.622;
 var TSet = HSet + RASet - (0.06571 * tSet) - 6.622;
 
 // adjust back to UTC

 var UTRise = TRise - lngHour;
 var UTSet = TSet - lngHour;
 if(UTRise < 0)
 {
  UTRise += 24;
 }
 else if(UTRise >= 24)
 {
  UTRise -= 24;
 }
 if(UTSet < 0)
 {
  UTSet += 24;
 }
 else if(UTSet >= 24)
 {
  UTSet -= 24;
 }
 
 // convert UT value to local time zone of latitude/longitude

 var RiseTime: float = UTRise + offset;
 var SetTime: float = UTSet + offset;
 
 if(RiseTime < 0)
 {
  RiseTime += 24;
 }
 else if(RiseTime >= 24)
 {
  RiseTime -= 24;
 }
 
 if(SetTime < 0)
 {
  SetTime += 24;
 }
 else if(SetTime >= 24)
 {
  SetTime -= 24;
 }

        // now you can use the RiseTime & SetTime as you wish
}