I have a Raspberry Pi that I didn't use regularly. I brushed off the dust and wanted to play around with the hardware again but I wasn't sure of the pin configuration. I then came across this handy utility that displays a summary of the Raspberry Pi hardware.
The pinout command gives this summary.
This is what the output looks like. From this you can see that the Raspberry Pi Model is 3B V1.2
Andrew's Tutorial Blog
Wednesday 17 July 2019
Saturday 15 June 2019
org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [java.lang.String arg0] in method [void ParametrizedTest.test(java.lang.String)].
I was experimenting with JUnit 5 and Parameterized Tests and hit the following error.
org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [java.lang.String arg0] in method [void ParametrizedTest.test(java.lang.String)].
This was the code that was used to trigger the error.
The issue is that the test method is annotated with both @Test and @ParameterizedTest. When using parameterized tests, you should only annotate your test method with @ParameterizedTest and not @Test. Removing the @Test annotation will prevent this error.
org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [java.lang.String arg0] in method [void ParametrizedTest.test(java.lang.String)].
This was the code that was used to trigger the error.
import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertTrue; public class ParametrizedTest { @Test @ParameterizedTest @MethodSource("stringProvider") void test(String string) { assertTrue(true); } public static StreamstringProvider() { return Stream.of("", null, "123"); } }
The issue is that the test method is annotated with both @Test and @ParameterizedTest. When using parameterized tests, you should only annotate your test method with @ParameterizedTest and not @Test. Removing the @Test annotation will prevent this error.
import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertTrue; public class ParametrizedTest { @ParameterizedTest @MethodSource("stringProvider") void test(String string) { assertTrue(true); } public static StreamstringProvider() { return Stream.of("", null, "123"); } }
Saturday 7 October 2017
Unity3d Change Costume of a 2d Character
In this tutorial we will look at re-skining a 2D character and applying a costume to them in Unity.
Here's what the main character looks like without any costume on.
The player can then change the costumes of the main character. Here's an example of main character re-skinned with different costumes.
Stop the Pop can be downloaded on The App Store: https://itunes.apple.com/us/app/stop-the-pop/id1166315634?ls=1&mt=8
Google Play: https://play.google.com/store/apps/details?id=com.stopthepopgame.stopthepop
Here's what the main character looks like without any costume on.
The player can then change the costumes of the main character. Here's an example of main character re-skinned with different costumes.
The player is split into different sprites. There is a sprite sheet for common player sprites for all costumes and a sprite sheet for each of the different parts of the character that can be re-skinned. This is what the main sprite sheet looks like.
There are also other sprite sheets for the Hats, shoes and facial hair. These are all stored in the Assets/Resources/Skins/Player directory.
The player then has different GameObjects for different areas to be skinned. In this case it is a Hat, Facial Hair and Shoes. The hierarchy can be seen here for the player.
Each of these components are just an empty GameObject with just a SpriteRenderer component.
You can see from the image from the inspector above that these don't have a sprite assigned to them. We are going to add a script to do this at runtime.
The script expects the sprite sheet to have been split up into sprites using the Sprite Editor. In the image below you can see that the different sprite sheets are split up into Elf, Leprechaun, Pirate, Santa, Viking sprites. This naming pattern has to be followed for other sprite sheets for the below code to work.
The following script does this by checking if a costume change is needed in the Update method, if it does the ChangeCostume method is called.
The ChangeCostume method works by loading in the facialHair.png, hats.png and shoes.png sprite sheets and stores them into sprite arrays. It gets the lists of SpriteRenderers to set. It also gets the sprite that the SpriteRenderer is going to be set to and sets it.
For example the following code loads in the sprite sheet Assets/Resources/skins/player/hats.png. The elements of the array are the Sprites in the image above. The renderers array contains all the SpriteRenderer components in the Player GameObject. We are only interested in the "Hat" SpriteRenderer so we call the GetSpriteRenderersWithName method and pass in "Hat". We then get the target sprite to set in the SpriteRenderer and we set it.
Sprite[] hatSprites = Resources.LoadAll ("skins/player/hats");
SpriteRenderer[] renderers = GetComponentsInChildren ();
List<SpriteRenderer> hatRenderersToSet = GetSpriteRenderersWithName ("Hat", renderers);
Sprite hatSprite = GetSpriteWithName (costumeName, hatSprites);
CustomisePlayer (hatSprite, hatRenderersToSet, costumeName);
Here's the entire code for the class.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Reskin : MonoBehaviour { public string costumeName; string currentCostume; void Start () { currentCostume = costumeName; ChangeCostume (currentCostume); } void Update () { //only change the costume if the costume has to be changed if (costumeName != currentCostume) { ChangeCostume (costumeName); currentCostume = costumeName; } } public void ChangeCostume(string costumeName) { Sprite[] facialHairSprites = Resources.LoadAll<Sprite>("skins/player/facialHair"); Sprite[] shoesSprites = Resources.LoadAll <Sprite> ("skins/player/shoes");
<Sprite> ("skins/player/hats"); Sprite[] hatSprites = Resources.LoadAll
SpriteRenderer> SpriteRenderer[] renderers = GetComponentsInChildren< ();
<SpriteRenderer> facialHairRenderersToSet = GetSpriteRenderersWithName ("FacialHair", renderers); List<SpriteRenderer> hatRenderersToSet = GetSpriteRenderersWithName ("Hat", renderers); List
<SpriteRenderer> shoeRenderersToSet = GetSpriteRenderersWithName ("Shoe", renderers); List
<SpriteRenderer> renderers, string costumeName) { Sprite facialHairSprite = GetSpriteWithName (costumeName, facialHairSprites); Sprite hatSprite = GetSpriteWithName (costumeName, hatSprites); Sprite shoeSprite = GetSpriteWithName (costumeName, shoesSprites); CustomisePlayer (hatSprite, hatRenderersToSet, costumeName); CustomisePlayer (facialHairSprite, facialHairRenderersToSet, costumeName); CustomisePlayer (shoeSprite, shoeRenderersToSet, costumeName); } /*** * Loop through the SpriteRenderers and set the correct sprite for them */ public void CustomisePlayer(Sprite sprite, List
<SpriteRenderer> GetSpriteRenderersWithName(string rendererName, SpriteRenderer[] renderers) { foreach (SpriteRenderer renderer in renderers) { renderer.sprite = sprite; } } /*** * Convenience method to get the Sprite with a name. */ Sprite GetSpriteWithName(string spriteName, Sprite[] sprites) { foreach (Sprite sprite in sprites) { if (sprite.name == costumeName) { return sprite; } } return null; } /*** * Get a list of all the SpriteRenderers with a specific name */ List
<SpriteRenderer> spriteRenderers = new List<SpriteRenderer> (); List
foreach (SpriteRenderer renderer in renderers) { if (renderer.name == rendererName) { spriteRenderers.Add (renderer); } } return spriteRenderers; } }
The costumes can be seen in this video at 0:39
Stop the Pop can be downloaded on The App Store: https://itunes.apple.com/us/app/stop-the-pop/id1166315634?ls=1&mt=8
Google Play: https://play.google.com/store/apps/details?id=com.stopthepopgame.stopthepop
Monday 25 September 2017
Iterate Over Lists in Kotlin
Create a list, add, remove and then iterate over a list.
This has the following output
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.
Right click on the Hierarchy, select UI -> Button
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.mThen 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
Subscribe to:
Posts (Atom)