Related
I have managed to save & restore single Objects or an Array of Strings/Integers using FileOutputStream, ObjectOutputStream (to .sav file - but it's not working on others as well), but this method doesn't work for an Array of Objects.
- I don't get any errors.
Any suggestions?
Thanks
An array of objects does not store objects themselves, instead it stores references to them, the equivalent of C/C++ pointers. When you save them you are saving just memory locations. When you reload there memory locations can now point to anything, moreover the objects they pointed to are no more there.
You would have to write a loop that writes serially all the objects in the array, but this can be long and/or difficult to correctly implement, especially for objects with variable sizes.
Or, you could wrap your array it into a object that implements Serializable interface and then you can write to disk and read back every type and combination of objects and arrays.
Just search Google for 'java serialization', it's very easy to implement.
As a side note, writing Objects directly into files is a bad practice not supported by Java. JVM decides order of object members at runtime, it could change at any time so you could end with unusable .sav files. Use serialization for that, it was meant for this purpose.
If anyone wants to have direct and easy access to real time HR data of Mi Band, for example to create a custom notification, exporting it to a PC or any other reason, here's an easy way.
- install Notify & Fitness app
- in Tasker, create new profile, event, system, intent received, action "com.mc.miband.heartRateGot"
- add a task with whatever action you want. The last measured HR is stored in variable "%value"
Whenever band measures a HR, this intent gets activated and %value is updated. For real-time measurements start a workout.
There are more intents available, the full list can be found here http://www.mibandnotify.com/help/tasker_send_intent.php
do all the results of those intents stored in "%value" variable ? or it changes with the action ? If it changes, how can I know all the other variables ?
please help I am trying to find those variables...
madkiran said:
do all the results of those intents stored in "%value" variable ? or it changes with the action ? If it changes, how can I know all the other variables ?
please help I am trying to find those variables...
Click to expand...
Click to collapse
Per the documentation (link in the first post), the result of intent is always stored in "%value" variable.
_mysiak_ said:
Per the documentation (link in the first post), the result of intent is always stored in "%value" variable.
Click to expand...
Click to collapse
yeah. value is working for all intents.. but I couldn't find that in the documentation'
Hi,
New to android programming, I try to build this game where the player hops between a number of ropes. For now; in addition to my main activity that I kept empty, I built two additional classes:
Ropes.java storing the coordinates of the ropes and their movement
Player.java storing the player's details
Can you advise me about the next step to join them ie (where to include the "attach function" that links the player to the rope in case he reaches it) as well as how to populate the main activity to organise the game.
Thank you.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
IntroductionBefore I begin, if you are not coming to this article from part 3 of the article, you can read here. For part 2 of the series, you can read here. If you have not seen part 1 either, you can click here. This is the fourth part of our HMS Unity Plugin 2.0 integration guide. This time I will be talking about other features of GameService: Leaderboards and SaveGame.
Normally, this part of the series was not planned, however, I thought that developers who might be interested in the other two parts of the GameService may be left off without a guide. Thus, I am adding this 4th part. I will be using a different game than the other three, but, I will try to be as helpful and as guiding as I can in this article as well, so you can adjust these two features to wherever you want to.
Small Warning Before We ProceedI will show the AGC side steps as much as I can, but this article also assumes that you have completed the part 1 app/project creation etc. and have an app running in contact with AGC and the plugin is ready to use. (You can just enable Banner Ads and tick test ads to test if the plugin is working.) Also, for the tests, make sure your account is registered as a sandbox test account. Details can be found in the docs link, if you have not done it yet.
My Game
As I said, I am using a different game for this part, but again a very simple hyper-casual one. You have a rock and 5 rock counts at the beginning of the game. You throw it in a projected trajectory to hit the balloons and you score points. Since the balloon generation and speed are determined at random, it is not as easy as it looks but it has a very simple logic. Its name is “Hit The Target”.
GameService — LeaderboardsLeaderboards let you create leaderboards in your game so that the players can compete and see how they rank in comparison to others. Huawei, like achievements, has its own UI to help you up setting up the leaderboard system. All you have to do is to make sure that GameService is enabled in AppGallery Connect (aka AGC), then create a leaderboard with some pre-defined rules and use the plugins easy-to-use managers to send/submit scores to leaderboards. It literally takes one line to submit the score in simple scenarios after the AGC is set up, thanks to HMS Unity Plugin 2.0.x.
AGC SideYou need to sign in to AGC and go to My apps. Then choose your app. My app in this case is “Hit The Target”. Go from “Distribute” tab to “Operate” tab and choose “Leaderboards”. Then click “New” button. You should see the screen below.
Add the details of your leaderboard. What kind of scores you want, how the formatting should be, min/max numbers that can be submitted etc. are all can be edited here. When you are done, click “Save”.
Now, we need to copy the ID of the leaderboard, so we can feed it to the plugin and use it in our game.
Do not release the leaderboards. Click “Obtain Resources” and copy the ID of the leaderboard you just created.
Unity SideNow head to Unity. Open the drop-down Huawei menu, click Kit Settings. Enable GameService (Account Kit will automatically be enabled and it is okay.) and go to the GameService tab.
Add a name to your leaderboard (which I used the same long name that I used in AGC) and paste the ID you copied in the previous step. Then, click create constant classes. Make sure to check the “Initialize On Start” button, or else you will have to write additional code.
Coding PhaseThe coding phase in Leaderboards is very easy. All you have to do is to submit the score to the leaderboard you have created.
Code:
HMSLeaderboardManager.Instance.SubmitScore(HMSLeaderboardConstants.HitTheTargetGeneralLeaderboard, GameManager.score /*score you want to submit*/);
You use the instance of HMSLeaderboardManager as usual and just call SubmitScore() function. Use the constant class that is automatically generated by the plugin to get which leaderboard you want to submit and enter the score type as the second parameter.
That's it for submitting the score, you should see it in the leaderboards and in the AGC.
One thing left for the integration. You should allow your users to see the leaderboard UI done by Huawei and check which leaderboards are there and which scores are submitted. This will help with the competitiveness of the game.
For that, all you need to do is to call again a one-liner code thanks to the plugin.
Code:
HMSLeaderboardManager.Instance.ShowLeaderboards();
I use this line inside a function and call that function in a UI Button onClick. Thus, whenever users click on the button, they are directed to the leaderboard UI and check which leaderboards are present. It should look like below.
GameService — SaveGameSaveGame takes more time than usual because of its very nature and purpose but it is a very powerful tool. As the name suggests, this kit helps you save the game progress of the player to the Huawei Cloud and lets the players load the saved progress to the current game. By this way, users never lose progress. It has its own UI to show saved games but it is also possible to implement your own UI, if you wish to do so.
You may save and load the game progress automatically in the background and set up a load-on-prompt system, or, like I would do it, save and load by the user’s actions. It is totally up to and to your game.
In my game, since it is a very simple game, I save the progress (score) and the rockCount and let the user save whenever s/he wishes. Later, the user can load this progress anytime in the pause menu and keep playing from that saved game. I will use the default Huawei UI, but if you wish to implement your own UI, I will leave links to docs where it talks about custom UI in the reference section. Make sure you check out that link, or alternatively, click here. I will talk about the code details later. First, let’s solve some error codes that you may possibly bump into.
Error Code 7219 in HMS GameService and Its Solution
If you have started the development already, you might have gotten the error 7219 in GameService SaveGame implementation and wonder why that could arise. It is because you need to agree to the user agreement in Drive Kit by Huawei located in https://cloud.huawei.com/ to be able to use SaveGame feature. The reason is that SaveGame saves the game files to the cloud using Drive Kit and if that agreement is not signed by your developer account, you will receive an error called 7219 and will not be able to proceed/test your code. Make sure you click the link, sign in, and agree to it. This is suggested before you start the implementation.
Coding PhaseBefore going into actual coding, let me mention this first. To let the users see the saved games in default UI and load the games with simple clicks, call the one-liner function below. (just like leaderboards) It will open the UI provided by AppGallery.
Code:
HMSSaveGameManager.Instance.ShowArchive();
Make sure you assign this code as an onClick to a UI button, or implement your own logic to access that UI.
Now, for the SaveGame we follow this doc, but on a Unity setting with the plugin. The order will not change but to see how you should code, bear with me. I will share the full new class in my game and explain/break down the code later. You do not have to open the docs, I will share the steps with you below, but always keep this doc in mind for the latest updates.
What needs to be done:The order in the official doc (written in Java):
Request DRIVE_DATA permission from the user and get ArchivesClient() object.
Get maxThumbnailSize and detailSize from the SDK. These must be requested, although you may not need them in your code.
Determine the details to save (your own parameters to save) and create ArchiveDetails object.
Write the archive metadata (such as the archive description, progress, and cover image) to the ArchiveSummaryUpdate object.
Call addArchive() method to save the game to the drive.
Notes:
You do not need to request a user permission in Unity side thanks to the plugin. It will be handled automatically.
Others will be talked about in detail below on a simple game I mentioned. If you have more complicated cases that cannot be adjusted, please refer to official documentation.
Coding in C#Let me share the code first.
Code:
using UnityEngine;
using HmsPlugin;
using HuaweiMobileServices.Game;
using System.Text;
public class ManagerOfSaveGame : MonoBehaviour
{
// Start is called before the first frame update
int maxThumbnailSize;
int detailSize;
GameStarterScript gameStarterScript;
void Start()
{
gameStarterScript = GameObject.Find("PauseButton").GetComponent<GameStarterScript>();
//HMSSaveGameManager.Instance.GetArchivesClient().LimitThumbnailSize.AddOnSuccessListener((x) => { });
HMSSaveGameManager.Instance.GetArchivesClient().LimitThumbnailSize.AddOnSuccessListener(LimitThumbnailSizeSuccess);
HMSSaveGameManager.Instance.GetArchivesClient().LimitDetailsSize.AddOnSuccessListener(LimitDetailSizeSuccess);
HMSSaveGameManager.Instance.SelectedAction = SelectedActionCreator;
HMSSaveGameManager.Instance.AddAction = AddActionCreator;
}
private void LimitDetailSizeSuccess(int thumbnailSize)
{
maxThumbnailSize = thumbnailSize;
}
private void LimitThumbnailSizeSuccess(int returnedDetailSize)
{
detailSize = returnedDetailSize;
}
private void SelectedActionCreator(ArchiveSummary archiveSummary)
{
//load your game
Debug.Log("YOU ENTERED SELECTED ACTION CALLBACK!");
long score = archiveSummary.CurrentProgress;
long rockCount = archiveSummary.ActiveTime;
if (GameManager.rockCount <= 0)
{
gameStarterScript.PlayGameWithParameters((int)score, (int)rockCount);
}
else
{
Debug.Log("Cannot load a finished game");
}
//start the game but change the parameters to load.
}
private void AddActionCreator(bool obj)
{
if(GameManager.rockCount != 0)
{
//save your game
string description = "Rock:" + GameManager.rockCount + " Score:" + GameManager.score;
long playedTime = GameManager.rockCount; //rock count
long progress = GameManager.score;
ArchiveDetails archiveContents = new ArchiveDetails.Builder().Build();
archiveContents.Set(Encoding.ASCII.GetBytes(progress + description + playedTime));
ArchiveSummaryUpdate archiveSummaryUpdate =
new ArchiveSummaryUpdate.Builder()
.SetActiveTime(playedTime)
.SetCurrentProgress(progress)
.SetDescInfo(description)
//.SetThumbnail(bitmap)
//.SetThumbnailMimeType(imageType)
.Build();
HMSSaveGameManager.Instance.GetArchivesClient().AddArchive(archiveContents, archiveSummaryUpdate, true).AddOnSuccessListener((archiveSummary) => {
string fileName = archiveSummary.FileName;
string archiveId = archiveSummary.Id;
//if you wanna use these you can. But this just indicates that it is successfully saved.
print("fileName is: " + fileName + " and archiveId is " + archiveId);
print("GamePlayer is: " + archiveSummary.GamePlayer + " and GameSummary is " + archiveSummary.GameSummary);
print("CurrentProgress is: " + archiveSummary.CurrentProgress + " and ActiveTime is " + archiveSummary.ActiveTime);
}).AddOnFailureListener((exception) => {
print("statusCode:" + exception.GetBaseException());
});
}
else
{
print("Game is over. Cannot save a finished game!");
}
}
}
Let's break down the code to understand.
First, I created a separate function called ManagerOfSaveGame.cs to manage SaveGames. I also create a game object in my scene and put the script in it. It has no appearance in the scene to the user. This is just to control it.
In the Start() function, I get the methods to request the parameters because documentation lists them as first thing to do. I will not use them later, so I just get the parameters and be done with it.
Then I create the corresponding functions in my script to SelectedAction and AddAction fields. First is to load the game from UI on click and the second is to save the game to the drive.
AddAction (Save your game)If I were you, I would copy the contents of the shown function and paste it to my game. Then, I would alter the parameters I want to alter. I first put an if check to see if the game is over. Since my game is a simple throw game and my rock count goes from 5 to 0, 0 rock count means a finished game. Although I could, I do not allow my users to save their games if they are already done because my UI technically allows users to access save game screen after the game is over.
You have several parameters that you can adjust. My code above follows the documentation order so you can be sure of that. What you should do with your game is to determine a description of the save games, a progress indicator, and if needed, the active time. I keep my score in score parameter and my rock count in active time parameter. Normally, I do not use time related functions but since the parameters that one can save is limited, I decided to use active time as an in-game save functionality. You can also do the same if you need. Typically, you can keep the level information, score information etc. in the progress “long” type parameter, and retrieve it when loading the game.
Rest goes according to “rules”. You can take them as is and adjust where needed. I do not use a Bitmap or an image to save with my save files. You can alternatively take a screenshot of the save moment and save it with the current progress. Huawei SDK allows that too, but I do not need it in my game.
Then you call AddArchive method as shown and success callback indicates that the game is saved. You need need to do anything with return parameter but I showed how to retrieve values nonetheless, if anyone ever needs it.
You can also get the exception message if the game cannot be saved for some reason.
SelectedAction (Load your game)This function will be automatically called when the users click on a previously saved game in default Huawei UI. Thus, what you need to do is to retrieve the values that you saved while saving the game and load the game according to your game logic.
For my case, I retrieve the rock count and score as shown, then start my scene with these parameter. They are set as static, so I can alter them easily.
You can adjust here depending on your game logic and how you want to load your game when the user clicks it. For example, if you kept the level information in progress parameter, then you try reloading that Unity scene to start that level from scratch.
Tips and Tricks
Do not publish the leaderboards if you want to keep testing them. Unless you are done with testing and want to publish your app in AppGallery, it should always be left as in Testable mod and releasing it will hinder your testing efforts.
Custom UI can be programmed, although Huawei already provides a UI for Leaderboards and SaveGames (for Achievements too!). Please refer to docs below in references to see the details.
When loading your game, beware that progress parameter is called “CurrentProgress”. If you called it something else, like I called just “progress”, make sure you retrieve the “CurrentProgress” field because there is no such field called “progress”.
ConclusionThat's it! You have successfully integrated Leaderboards and SaveGame features. They have a wide variety of use cases and I know that mine are simple; but at least, I believe, I gave you the insight so that you can adapt these kits to your game and draw more users.
I hope that this article series has been helpful for you. You can always ask questions below, if you have anything unanswered in your mind.
Good luck on the store and see you in my other articles!
References
HMS Unity Plugin 2.0 Branch (Github Page)
GameService Result Codes Page
SaveGame Docs
Leaderboard Docs
Documentation of every kit in Huawei Docs (Links are present in the GitHub readme)
Original Source
The emergence of AR technology has allowed us to interact with our devices in a new and unexpected way. With regard to smart device development, from PCs to mobile phones and beyond, the process has been dramatically simplified. Interactions have been streamlined to the point where only slides and taps are required, and even children as young as 2 or 3 can use devices.
Rather than having to rely on tools like keyboards, mouse devices, and touchscreens, we can now control devices in a refreshingly natural and easy way. Traditional interactions with smart devices have tended to be cumbersome and unintuitive, and there is a hunger for new engaging methods, particularly among young people. Many developers have taken heed of this, building practical but exhilarating AR features into their apps. For example, during live streams, or when shooting videos or images, AR-based apps allow users to add stickers and special effects with newfound ease, simply by striking a pose; in smart home scenarios, users can use specific gestures to turn smart home appliances on and off, or switch settings, all without any screen operations required; or when dancing using a video game console, the dancer can raise a palm to pause or resume the game at any time, or swipe left or right to switch between settings, without having to touch the console itself.
So what is the technology behind these groundbreaking interactions between human and devices?
HMS Core AR Engine is a preferred choice among AR app developers. Its SDK provides AR-based capabilities that streamline the development process. This SDK is able to recognize specific gestures with a high level of accuracy, output the recognition result, and provide the screen coordinates of the palm detection box, and both the left and right hands can be recognized. However, it is important to note that when there are multiple hands within an image, only the recognition results and coordinates from the hand that has been most clearly captured, with the highest degree of confidence, will be sent back to your app. You can switch freely between the front and rear cameras during the recognition.
Gesture recognition allows you to place virtual objects in the user's hand, and trigger certain statuses based on the changes to the hand gestures, providing a wealth of fun interactions within your AR app.
The hand skeleton tracking capability works by detecting and tracking the positions and postures of up to 21 hand joints in real time, and generating true-to-life hand skeleton models with attributes like fingertip endpoints and palm orientation, as well as the hand skeleton itself.
AR Engine detects the hand skeleton in a precise manner, allowing your app to superimpose virtual objects on the hand with a high degree of accuracy, including on the fingertips or palm. You can also perform a greater number of precise operations on virtual hands and objects, to enrich your AR app with fun new experiences and interactions.
Getting StartedPrepare the development environment as follows:
JDK: 1.8.211 or later
Android Studio: 3.0 or later
minSdkVersion: 26 or later
targetSdkVersion: 29 (recommended)
compileSdkVersion: 29 (recommended)
Gradle version: 6.1.1 or later (recommended)
Before getting started, make sure that the AR Engine APK is installed on the device. You can download it from AppGallery. Click here to learn on which devices you can test the demo.
Note that you will need to first register as a Huawei developer and verify your identity on HUAWEI Developers. Then, you will be able to integrate the AR Engine SDK via the Maven repository in Android Studio. Check which Gradle plugin version you are using, and configure the Maven repository address according to the specific version.
App Development1. Check whether AR Engine has been installed on the current device. Your app can run properly only on devices with AR Engine installed. If it is not installed, you need to prompt the user to download and install AR Engine, for example, by redirecting the user to AppGallery. The sample code is as follows:
Code:
boolean isInstallArEngineApk =AREnginesApk.isAREngineApkReady(this);
if (!isInstallArEngineApk) {
// ConnectAppMarketActivity.class is the activity for redirecting users to AppGallery.
startActivity(new Intent(this, com.huawei.arengine.demos.common.ConnectAppMarketActivity.class));
isRemindInstall = true;
}
2. Initialize an AR scene. AR Engine supports the following five scenes: motion tracking (ARWorldTrackingConfig), face tracking (ARFaceTrackingConfig), hand recognition (ARHandTrackingConfig), human body tracking (ARBodyTrackingConfig), and image recognition(ARImageTrackingConfig).
Call ARHandTrackingConfig to initialize the hand recognition scene.
Code:
mArSession = new ARSession(context);
ARHandTrackingConfig config = new ARHandTrackingconfig(mArSession);
3. You can set the front or rear camera as follows after obtaining an ARhandTrackingconfig object.
Code:
Config.setCameraLensFacing(ARConfigBase.CameraLensFacing.FRONT);
4. After obtaining config, configure it in ArSession, and start hand recognition.
Code:
mArSession.configure(config);
mArSession.resume();
5. Initialize the HandSkeletonLineDisplay class, which draws the hand skeleton based on the coordinates of the hand skeleton points.
Code:
Class HandSkeletonLineDisplay implements HandRelatedDisplay{
// Methods used in this class are as follows:
// Initialization method.
public void init(){
}
// Method for drawing the hand skeleton. When calling this method, you need to pass the ARHand object to obtain data.
public void onDrawFrame(Collection<ARHand> hands,){
// Call the getHandskeletonArray() method to obtain the coordinates of hand skeleton points.
Float[] handSkeletons = hand.getHandskeletonArray();
// Pass handSkeletons to the method for updating data in real time.
updateHandSkeletonsData(handSkeletons);
}
// Method for updating the hand skeleton point connection data. Call this method when any frame is updated.
public void updateHandSkeletonLinesData(){
// Method for creating and initializing the data stored in the buffer object.
GLES20.glBufferData(..., mVboSize, ...);
//Update the data in the buffer object.
GLES20.glBufferSubData(..., mPointsNum, ...);
}
}
6. Initialize the HandRenderManager class, which is used to render the data obtained from AR Engine.
Code:
Public class HandRenderManager implements GLSurfaceView.Renderer{
// Set the ARSession object to obtain the latest data in the onDrawFrame method.
Public void setArSession(){
}
}
7. Initialize the onDrawFrame() method in the HandRenderManager class.
Code:
Public void onDrawFrame(){
// In this method, call methods such as setCameraTextureName() and update() to update the calculation result of ArEngine.
// Call this API when the latest data is obtained.
mSession.setCameraTextureName();
ARFrame arFrame = mSession.update();
ARCamera arCamera = arFrame.getCamera();
// Obtain the tracking result returned during hand tracking.
Collection<ARHand> hands = mSession.getAllTrackables(ARHand.class);
// Pass the obtained hands object in a loop to the method for updating gesture recognition information cyclically for processing.
For(ARHand hand : hands){
updateMessageData(hand);
}
}
8. On the HandActivity page, set a render for SurfaceView.
Code:
mSurfaceView.setRenderer(mHandRenderManager);
Setting the rendering mode.
mSurfaceView.setRenderMode(GLEurfaceView.RENDERMODE_CONTINUOUSLY);
Physical controls and gesture-based interactions come with unique advantages and disadvantages. For example, gestures are unable to provide the tactile feedback provided by keys, especially crucial for shooting games, in which pulling the trigger is an essential operation; but in simulation games and social networking, gesture-based interactions provide a high level of versatility.
Gestures are unable to replace physical controls in situations that require tactile feedback, and physical controls are unable to naturally reproduce the effects of hand movements and complex hand gestures, but there is no doubt that gestures will become indispensable to future smart device interactions.
Many somatosensory games, smart home appliances, and camera-dependent games are now using AR to offer a diverse range of smart, convenient features. Common gestures include eye movements, pinches, taps, swipes, and shakes, which users can strike without having to learn additionally. These gestures are captured and identified by mobile devices, and used to implement specific functions for users. When developing an AR-based mobile app, you will need to first enable your app to identify these gestures. AR Engine helps by dramatically streamlining the development process. Integrate the SDK to equip your app with the capability to accurately identify common user gestures, and trigger corresponding operations. Try out the toolkit for yourself, to explore a treasure trove of powerful, interesting AR features.
References
AR Engine Development Guide
AR Engine Sample Code