Monday, 25 September 2017

Iterate Over Lists in Kotlin

Create a list, add, remove and then iterate over a list.

fun main(args: Array<String>) {
    var list = mutableListOf<String>("zero", "one", "two", "three", "four")

    list.add("five")
    list.remove("zero")

    for(str in list) {
        println("value = " + str)
    }
}


This has the following output

value = one
value = two
value = three
value = four
value = five

Iterate Over an Array in Kotlin

In this example we create an array, called array. The contents of the array are created by using the Kotlin arrayOf function. To iterate over the array, we use a for loop.

fun main(args: Array<String>) {
    var array = arrayOf("one", "two", "three", "four")
    for(str in array) {
        println("value = " + str)
    }
}



This produces the following output:
value = one
value = two
value = three
value = four


Friday, 28 April 2017

Unity3d - Share Image on iOS and Android

The purpose of this code is to read in the title and body of the text getting shared. Then it takes a screenshot of the current screen in the game, saves it to a file, and then, depending on the platform, the screenshot is sent to the native Android or iOS share libraries with the title and body of the text that will be shared. This will present a native pop-up on the device showing what applications are available to share the media. This code is adapted from this github project: https://github.com/ChrisMaire/unity-native-sharing Start with an empty project and an empty scene

 The following directory structure should be created. A Scripts directory for the Share script was created for organizational purposes but this step is not necessary. It is very important to create the Plugins/iOS directories exactly as shown below.




Create the code to Share content

A Create a class called Share.cs under Scripts with the following contents.

using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using UnityEngine;

public class Share : MonoBehaviour {
 public string title;
 public string bodyText;

 public void SaveAndShareImage() {
  string destination = Path.Combine(Application.persistentDataPath, "test.png");
  SaveImage (destination);
  ShareImage (destination, title, 
   bodyText);
 }

 void ShareImage(string imageLocation, string titleText, string bodyText) {
  #if UNITY_IOS
  CallSocialShareAdvanced(bodyText, titleText, "", imageLocation);
  #endif

  #if UNITY_ANDROID
  AndroidJavaClass intentClass = new AndroidJavaClass("android.content.Intent");
  AndroidJavaObject intentObject = new AndroidJavaObject("android.content.Intent");
  intentObject.Call("setAction", intentClass.GetStatic("ACTION_SEND"));
  AndroidJavaClass uriClass = new AndroidJavaClass("android.net.Uri");
  AndroidJavaObject uriObject = uriClass.CallStatic("parse","file://" + imageLocation);
  intentObject.Call("putExtra", intentClass.GetStatic("EXTRA_STREAM"), uriObject);
  intentObject.Call("putExtra", intentClass.GetStatic("EXTRA_TEXT"), bodyText);
  intentObject.Call("putExtra", intentClass.GetStatic("EXTRA_SUBJECT"), titleText);
  intentObject.Call("setType", "image/jpeg");
  AndroidJavaClass unity = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
  AndroidJavaObject currentActivity = unity.GetStatic("currentActivity");

  currentActivity.Call("startActivity", intentObject);
  #endif
 }

 void SaveImage(string destination) {
  Texture2D screenTexture = new Texture2D(Screen.width, Screen.height, TextureFormat.RGB24, true);
  screenTexture.ReadPixels(new Rect(0, 0, Screen.width, Screen.height),0,0);
  screenTexture.Apply();
  byte[] dataToSave = screenTexture.EncodeToPNG();
  File.WriteAllBytes(destination, dataToSave);
 }

 #if UNITY_IOS
 public struct ConfigStruct
 {
  public string title;
  public string message;
 }

 [DllImport ("__Internal")] private static extern void showAlertMessage(ref ConfigStruct conf);

 public struct SocialSharingStruct
 {
  public string text;
  public string url;
  public string image;
  public string subject;
 }

 [DllImport ("__Internal")] private static extern void showSocialSharing(ref SocialSharingStruct conf);

 public static void CallSocialShare(string title, string message)
 {
  ConfigStruct conf = new ConfigStruct();
  conf.title  = title;
  conf.message = message;
  showAlertMessage(ref conf);
 }


 public static void CallSocialShareAdvanced(string defaultTxt, string subject, string url, string img)
 {
  SocialSharingStruct conf = new SocialSharingStruct();
  conf.text = defaultTxt;
  conf.url = url;
  conf.image = img;
  conf.subject = subject;

  showSocialSharing(ref conf);
 }
 #endif
}

Create an iOS plugin

At this stage, there is enough code to get Android sharing working. Unity requires a native iOS plugin to do native sharing. An iOS plugin with two more files need to be created. The files that need to be created are:
Plugins/iOS/iOSNativeShare.h
Plugins/iOS/iOSNativeShare.m
Then make sure that these files have the following Objective C code. The following code was taken from https://github.com/ChrisMaire/unity-native-sharing Plugins/iOS/iOSNativeShare.h
#import "UnityAppController.h"

@interface iOSNativeShare : UIViewController
{
    UINavigationController *navController;
}


struct ConfigStruct {
    char* title;
    char* message;
};

struct SocialSharingStruct {
    char* text;
    char* url;
    char* image;
    char* subject;
};


#ifdef __cplusplus
extern "C" {
#endif
    
    void showAlertMessage(struct ConfigStruct *confStruct);
    void showSocialSharing(struct SocialSharingStruct *confStruct);
    
#ifdef __cplusplus
}
#endif


@end



Next, add the following code to Plugins/iOS/iOSNativeShare.m






#import "iOSNativeShare.h"

@implementation iOSNativeShare{
}

#ifdef UNITY_4_0 || UNITY_5_0

#import "iPhone_View.h"

#else

extern UIViewController* UnityGetGLViewController();

#endif

+(id) withTitle:(char*)title withMessage:(char*)message{
 
 return [[iOSNativeShare alloc] initWithTitle:title withMessage:message];
}

-(id) initWithTitle:(char*)title withMessage:(char*)message{
 
 self = [super init];
 
 if( !self ) return self;
 
 ShowAlertMessage([[NSString alloc] initWithUTF8String:title], [[NSString alloc] initWithUTF8String:message]);
 
 return self;
 
}

void ShowAlertMessage (NSString *title, NSString *message){
 
 UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
                       
                       message:message
                       
                       delegate:nil
                       
                       cancelButtonTitle:@"OK"
                       
                       otherButtonTitles: nil];
 
 [alert show];
 
}

+(id) withText:(char*)text withURL:(char*)url withImage:(char*)image withSubject:(char*)subject{
 
 return [[iOSNativeShare alloc] initWithText:text withURL:url withImage:image withSubject:subject];
}

-(id) initWithText:(char*)text withURL:(char*)url withImage:(char*)image withSubject:(char*)subject{
 
 self = [super init];
 
 if( !self ) return self;
 
 
 
    NSString *mText = text ? [[NSString alloc] initWithUTF8String:text] : nil;
 
    NSString *mUrl = url ? [[NSString alloc] initWithUTF8String:url] : nil;
 
    NSString *mImage = image ? [[NSString alloc] initWithUTF8String:image] : nil;
 
    NSString *mSubject = subject ? [[NSString alloc] initWithUTF8String:subject] : nil;
 
 
 NSMutableArray *items = [NSMutableArray new];
 
 if(mText != NULL && mText.length > 0){
  
  [items addObject:mText];
  
 }
 
 if(mUrl != NULL && mUrl.length > 0){
  
  NSURL *formattedURL = [NSURL URLWithString:mUrl];
  
  [items addObject:formattedURL];
  
 }
 if(mImage != NULL && mImage.length > 0){
  
  if([mImage hasPrefix:@"http"])
  {
   NSURL *urlImage = [NSURL URLWithString:mImage];
   
            NSError *error = nil;
            NSData *dataImage = [NSData dataWithContentsOfURL:urlImage options:0 error:&error];
            
            if (!error) {
                UIImage *imageFromUrl = [UIImage imageWithData:dataImage];
                [items addObject:imageFromUrl];
            } else {
                ShowAlertMessage(@"Error", @"Cannot load image");
            }
        }
        else if ( [self isStringValideBase64:mImage]){
            NSData* imageBase64Data = [[NSData alloc]initWithBase64Encoding:mImage];
            UIImage* image = [UIImage imageWithData:imageBase64Data];
            if (image!= nil){
                [items addObject:image];
            }
            else{
                ShowAlertMessage(@"Error", @"Cannot load image");
            }
        }
        else{
   NSFileManager *fileMgr = [NSFileManager defaultManager];
   if([fileMgr fileExistsAtPath:mImage]){
    
    NSData *dataImage = [NSData dataWithContentsOfFile:mImage];
    
    UIImage *imageFromUrl = [UIImage imageWithData:dataImage];
    
    [items addObject:imageFromUrl];
   }else{
    ShowAlertMessage(@"Error", @"Cannot find image");
   }
  }
 }
 
 UIActivityViewController *activity = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:Nil];
 
    if(mSubject != NULL) {
        [activity setValue:mSubject forKey:@"subject"];
    } else {
        [activity setValue:@"" forKey:@"subject"];
    }
 
 UIViewController *rootViewController = UnityGetGLViewController();
    //if iPhone
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
          [rootViewController presentViewController:activity animated:YES completion:Nil];
    }
    //if iPad
    else {
        // Change Rect to position Popover
        UIPopoverController *popup = [[UIPopoverController alloc] initWithContentViewController:activity];
        [popup presentPopoverFromRect:CGRectMake(rootViewController.view.frame.size.width/2, rootViewController.view.frame.size.height/4, 0, 0)inView:rootViewController.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
    }
    return self;
}

-(BOOL) isStringValideBase64:(NSString*)string{
    
    NSString *regExPattern = @"^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$";
    
    NSRegularExpression *regEx = [[NSRegularExpression alloc] initWithPattern:regExPattern options:NSRegularExpressionCaseInsensitive error:nil];
    NSUInteger regExMatches = [regEx numberOfMatchesInString:string options:0 range:NSMakeRange(0, [string length])];
    return regExMatches != 0;
}

# pragma mark - C API
iOSNativeShare* instance;

void showAlertMessage(struct ConfigStruct *confStruct) {
 instance = [iOSNativeShare withTitle:confStruct->title withMessage:confStruct->message];
}

void showSocialSharing(struct SocialSharingStruct *confStruct) {
 instance = [iOSNativeShare withText:confStruct->text withURL:confStruct->url withImage:confStruct->image withSubject:confStruct->subject];
}

@end

Hook up the code to a button press

Now, to hook up this code to your application, you can trigger it from a button press.

Right click on the Hierarchy, select UI -> Button



That will create a Canvas and a Button in the Hierarchy, and some embedded text in the button. What we are interested in here is adding the Share script to the button. 
Select the button in the hierarchy and in the inspector, click "Add Component" and type Share and click on the script.


Now there are two text fields, one for the title of the share content and one for the share body text.


Type whatever you want to be in the body and in the title of the shared content in here.

Now click on "On Click ()" "+" button on the Button component in the inspector.



Click on the "None (Ob"... text field and select Button. Then in the dropdown with "No Function", select Share -> SendAndShareImage()


This makes the Share code to get triggered when the button is clicked. 

Next deploy the game to an iOS or Android device, and you will be able to share an image with a title and text


Saturday, 25 June 2016

Set up a Git Repository on Raspberry Pi with SSH

I have a Raspberry Pi on my local network and I wanted to set it up as a git repository on my local network.

First thing I did was SSH onto my Raspberry Pi and create a directory for the repo.
mkdir <project name>

Next I needed to set up the Git repo. 
cd <project name>
git init --bare

Now the repo was set up and I could clone it to any device on my network.
I cloned the repo onto my mac
git clone ssh://<username>@<raspberry pi ip or hostname>:<path to repo>/<repo name>

The repo was now cloned on my mac, I could make local commits. But to push to the remote repo over SSH, I needed to specify what branch was needed. When I performed git init --bare, no branches were actually created, so I needed to create and push to a new remote branch.
git push origin master



Thursday, 29 October 2015

Mac won't power up

Problem

Issue with powering on MacBook Pro Retina.
Tried a hard reset (hold power button down for 10 seconds).
This did not solve the issue.
When the power button was pressed, the Apple logo light on the laptop was on.
Toggling Caps-Lock did not power on or off the light in the Caps-Lock key.
There was some heat coming from the laptop when touching the bottom of if.

Solution

The solution was to reset the NVRAM.
To do this, do a hard poweroff (hold down the power button for about 10 seconds). Verify that the Apple logo on the laptop lid is not on.
Now press the power button.
Immediately after pressing the power button hold down the following keys. Command, Option, P and R. You will then hear the start up sound play.
Keep holding the keys down until the startup sound plays again, or till the login screen is displayed.


More details about this can be found on the Apple website https://support.apple.com/en-us/HT204063

Sunday, 19 July 2015

Unity 3D - Red Lines in Scene View when importing UI Prefab

I had an issue importing a UI prefab using Unity 5. When I imported it, there were a lot a red lines over the scene. The prefab was not showing up in the scene view or game view. It appears as if the UI prefab is corrupted or is invalid.


The problem here is that the UI prefab was not imported into the UI canvas. 
This is fixed by:
  1. Removing the prefab from the scene
  2. Add a UI Canvas to the scene
  3. Add the prefab to the Canvas

Saturday, 20 June 2015

Auto Position Game Objects in Random Positions in Unity

I was working on a 2D game in Unity and I wanted a lot of game objects placed randomly in the level but I didn't want to spend a lot of time laying out each individual game object. I wanted to generate a lot of clouds in a level but without the hassle of manually laying them all out.

I achieved this by creating and attaching a script that would automatically layout the position of all the game objects in the level.


This is what an example of what the level looks like.

I created a C# script called GameObjectGenerator.cs.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

/***
 * Programatically generate a list of GameObjects at random positions 
 * between xOffset to xLength, yOffset to yLength and zOffset to zLength.
 */
public class GameObjectGenerator : MonoBehaviour {
 
 /***
  * The maximum number of objects to get generated
  */
 public int maxObjects = 10;

 /***
  * The type of game object to generate
  */
 public GameObject generatedObjectType;

 /***
  * The x-axis offset to position the game objects
  */
 public float xOffset = 0f;
 /***
  * The y-axis offset to position the game objects
  */
 public float yOffset = 0f;
 /***
  * The z-axis offset to position the game objects
  */
 public float zOffset = 0f;

 /***
  * The length of the level on the x-axis
  */
 public float xLength = 10f;
 /***
  * The length of the level on the y-axis
  */
 public float yLength = 10f;
 /***
  * The length of the level on the z-axis
  */
 public float zLength = 10f;

 /***
  * Internal list of all the generated game objects. 
  */
 private List gameObjects;

 void Start () {
  gameObjects = new List();
  generateGameObjects ();
 }
 
 public void generateGameObjects() {
  for (int i = 0; i < maxObjects; i++) {
   //get a random value between the offset and the length
   float xPos = Mathf.Clamp(xOffset + Random.value * xLength, xOffset, xLength);
   float yPos = Mathf.Clamp(yOffset + Random.value * yLength, yOffset, yLength);
   float zPos = Mathf.Clamp(zOffset + Random.value * zLength, zOffset, zLength);

   Vector3 position = new Vector3 (xPos, yPos, zPos);

   GameObject gameObject = (GameObject)Instantiate (generatedObjectType, position, Quaternion.identity);
   gameObjects.Add(gameObject);
  }
 }
}



First thing to do to get this working, is to create an empty game object and added it to the scene. I called it CloudGenerator. Attach the GameObjectGenerator script to the CloudGenerator. Once the script is attached, you can see all the parameters that can be changed for the script.

 
Description
Max Objects is the number of Game Objects that are going to be instantiated in the level.
Generated Object Type is the game object that is going to be generated throughout the level. In my case, it was a cloud sprite Game Object.
X Offset is the position on the X-Axis where the game objects will begin to get placed.
Y Offset is the position on the Y-Axis where the game objects will begin to get placed.
Z Offset is the position on the Z-Axis where the game objects will begin to get placed.
X Length is the length of the level in the X-Axis.
Y Length is the length of the level in the Y-Axis.
Z Length is the length of the level in the Z-Axis.

So from the example above, there will be 100 Cloud objects created in random positions from (-42, 4, -5) to (300, 7, 50).

This also brings parallax to the game since objects are created at random values on the Z-Axis. If you don't want parallax in your game, just set Z Offset and Z Length to 0.