When the authenticated handler fires on return from Game Center, the local player is listed with displayName = "Me" and the alias is the player's username. However I'd like to display the user's full name instead, so want the actual displayName, not "Me".
Is there a way of specifying I want the full name, not "Me"?
I prefer showing the alias, especially when matching with random players but both the alias and the displayName require that the player's authentication be complete to start returning their appropriate values.
In order for the authentication process to start, you have to set the local player's authentication handler. Simply setting it starts the process and the method will be called within a few seconds. After that the local player's alias and displayName should be the right ones.
for example:
class YourGameCenterManager:GKGameCenterControllerDelegate,
GKLocalPlayerListener
{
var localGCAccount: GKLocalPlayer!
var active = false
init()
{
localGCAccount = GKLocalPlayer.localPlayer()
localGCAccount?.authenticateHandler = gameCenterAuthentication
}
func gameCenterAuthentication(gameCenterVC :UIViewController?, err:NSError?)
{
if gameCenterVC != nil
{
// Game center wants to display a sign-on view ...
// note: I personally never got this to actually happen
}
else if localGCAccount?.authenticated ?? false
{
if not(active)
{
active = true
localGCAccount?.unregisterAllListeners()
localGCAccount?.registerListener(self)
// ... whatever else you need to do when Game Center is ready
// at this point localGCAccount's alias and displayName should be ok
}
}
else if active
{
//... Game Center just went bad ... do what you have to to to handle it
active = false
}
}
Related
I have implemented the Zoom iOS SDK to work with a custom UI. Everything works just as its supposed to but I haven't been able to figure out how I can get the userID of the currently active user.
I have implemented the below delegate method which tells about the current active video user, but unfortunately it shows all the other participants in the meeting except me.
func onSinkMeetingActiveVideo(_ userID: UInt) {
if let service = MobileRTC.shared().getMeetingService(), let username = service.userInfo(byID: userID)?.userName {
print("\(#function) : \(userID) : \(username)")
}
}
I need to know who is the current active user even if its me who is talking.
You can retrieve this kind of information from meeting service MobileRTCMeetingService.
MobileRTCMeetingService
func getActiveUserId() -> UInt? {
if let meetingService = MobileRTC.shared().getMeetingService() {
return meetingService.activeUserID()
}
return nil
}
Extra note: in Zoom there is also the concept of Pinned User that overrides active user in active video cell.
Pinned user id can be retrieved in this way:
func getPinnedUserId() -> UInt? {
if let meetingService = MobileRTC.shared().getMeetingService(), let userList = meetingService.getInMeetingUserList(){
for userId in userList {
if let userId = userId as? UInt, meetingService.isUserPinned(userId) {
return userId
}
}
return nil
}
return nil
}
So in order to establish which is the user id of the video in active video cell you have to check both, giving priority to pinned user.
let currentVideoUserId = getPinnedUserId() ?? getActiveUserId()
During the meeting you will never been the active user in your own video cell because even if your are speaking, you will continue to see the other person in active video cell.
On the other side if you are interested to know who is talking then you have to retrieve the user list and check the audioStatus [MobileRTCAudioStatus].
MobileRTCAudioStatus
MobileRTCMeetingUserInfo
Just pay attention that you can have more than one user speaking at the same time.
There is also another callback that can be useful if you are interested in active speaker user: it is the onSinkMeetingActiveVideoForDeck in MobileRTCVideoServiceDelegate
MobileRTCVideoServiceDelegate
According to the documentation it should be fired every time that there is a new speaker. It is used by ZOOM UI for changing the yellow frame around the active speaker user.
I according to the documentation, in order to get the current active video user info you should use the following class: MobileRTCMeetingUserInfo.
Check the doc for the video status class MobileRTCVideoStatus: https://marketplacefront.zoom.us/sdk/meeting/ios/interface_mobile_r_t_c_video_status.html
and you will see that is related with the MobileRTCMeetingUserInfo:
https://marketplacefront.zoom.us/sdk/meeting/ios/interface_mobile_r_t_c_meeting_user_info.html
On that class you will find info of the current user.
Hope you can figure out your problem!
Regards!
Gastón Montes.
My question is the following:
Can I change the HKWorkoutConfiguration.activityType during a
HKWorkoutSession or does each HKWorkoutSession has to have its own
HKWorkoutConfiguration.activityType?
I want to create a workout app where you can create a workout consisting of different sets with different activity types. For example a Shadowing Boxing Workout, consisting of 3 sets of Boxing and 3 sets of Kickboxing (Boxing and Kickboxing are the different activities).
Ideally I would just start the HKWorkoutSession once at the beginning and end it after all sets for each activity are done, changing the HKWorkoutConfiguration.activityType in-between.
My current approach is based on the sample provided by Apple: https://developer.apple.com/documentation/healthkit/workouts_and_activity_rings/speedysloth_creating_a_workout
I adjusted the startWorkout() method to startWorkout(for type: String). It now looks like this:
// Start the workout.
func startWorkout(for type: String) {
// Start the timer.
setUpTimer()
self.running = true
// Create the session and obtain the workout builder.
/// - Tag: CreateWorkout
do {
session = try HKWorkoutSession(healthStore: healthStore, configuration: self.workoutConfiguration(for: type))
builder = session.associatedWorkoutBuilder()
} catch {
// Handle any exceptions.
return
}
// Setup session and builder.
session.delegate = self
builder.delegate = self
// Set the workout builder's data source.
/// - Tag: SetDataSource
builder.dataSource = HKLiveWorkoutDataSource(healthStore: healthStore,
workoutConfiguration: workoutConfiguration(for: type))
// Start the workout session and begin data collection.
/// - Tag: StartSession
session.startActivity(with: Date())
builder.beginCollection(withStart: Date()) { (success, error) in
// The workout has started.
}
print("New Workout has started")
}
In the method I get the respective activity by workoutConfiguration(for: type) which looks up the right activity from a string.
After a set is done (e.g. the boxing set), I end the session and start a new workout and session for the new set with the new activity.
My problem with the current approach is that I need to end the current HKWorkoutSession before I start the new one. But ending the session the way its done in the example does not execute immediately and therefore the new set of the workouts starts without saving the old set to the HKStore with the right activity.
Therefore I thought I would be nice to start the session just once and switch activityTypes in-between. However, I don't know if it is possible (maybe complications with HKStore) and how it is done.
Or is there any other smart way of doing things to achieve this?
I'm just starting out with iOS Programming.
Any help is greatly appreciated!
Disclaimer: This isn't a full answer, but an approach I'd take to address the problem.
Look at the workoutSession(_:didChangeTo:from:date:) method. Documentation Link
When one type of workout ends that method will receive a notification. As long as you haven't called session.end() the workout session should still be active. Use your healthStore instance to save the previous workout session before starting a new one.
It could look like this;
func workoutSession(_ workoutSession: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date: Date) {
if toState == .ended {
workoutBuilder.endCollection(withEnd: date) { (success, error) in
// collection has ended for one type of workout
self.workoutBuilder.finishWorkout { (workout, error) in
// unwrap the optional `workout`
guard let completedWorkout = workout else { return }
// save workout to health store
healthStore.save(completedWorkout) { (success, error) in
if success {
// begin a new workout by calling your method to start a new workout with a new configuration here
startWorkout(for type: String)
}
}
}
}
}
}
That is likely going to cause issues with your Timer since you're calling setUpTimer again but you can work with that. Also this doesn't address the point when the user is completely done with their workout and doesn't want to start a new workout type.
Recently I've been writing a game that requires the Pitch of the device in order to move the character. However, to make it so the user doesn't have to play the game with the device fixed in one starting point every time, every time the user presses play I want the app to acquire the initial tilt. This does not work however, I've added a test button that is suppose to run the code:
func recordTilt() {
InitialTilt = MovementManager.deviceMotion?.attitude
print(InitialTilt)
}
The problem with this is that whenever the button is pressed, InitialTilt will return nil. However, if InitialTilt is run in a the Loop it will return a value every time.
Movement Manager Loop:
func setup() {
MovementManager = CMMotionManager()
MovementManager.deviceMotionUpdateInterval = 0.1
MovementManager.startDeviceMotionUpdates()
InintialTilt = Movementmanager.deviceMotion?.attitude
}
func movementManaging() {
//...
InintialTilt = Movementmanager.deviceMotion?.attitude // returns every
time
//...
}
func update() {
movementManaging()
}
Can someone please help explain to me why the (InintialTilt = Movementmanager.deviceMotion?.attitude) only returns a value while it is in a loop.
(Note: The movementManaging is basically a loop that controls the player's movements, if one were to expand the ...'s all they would get are a bunch of if methods that keep the player on screen. + InintialTilt = Movementmanager.deviceMotion?.attitude)
I have successfully connected a steel series Nimbus dual analog controller to use for testing in both my iOS and tvOS apps. But I am unsure about how to properly set up the valueChangeHandler portion of my GCController property.
I understand so far that there are microGamepad, gamepad and extendedGamepad classes of controllers and the differences between them. I also understand that you can check to see if the respective controller class is available on the controller connected to your device.
But now I am having trouble setting up valueChangeHandler because if I set the three valueChangeHandler portions like so, then only the valueChangeHandler that works is the last one that was loaded in this sequence:
self.gameController = GCController.controllers()[0]
self.gameController.extendedGamepad?.valueChangedHandler = { (gamepad, element) -> Void in
if element == self.gameController.extendedGamepad?.leftThumbstick {
//Never gets called
}
}
self.gameController.gamepad?.valueChangedHandler = { (gamepad, element) -> Void in
if element == self.gameController.gamepad?.dpad {
//Never gets called
}
}
self.gameController.microGamepad?.valueChangedHandler = { (gamepad, element) -> Void in
if element == self.gameController.microGamepad?.dpad {
//Gets called
}
}
If I switch them around and call self.gameController.extendedGamepad.valueChangeHandler... last, then those methods will work and the gamepad and microGamepad methods will not.
Anyone know how to fix this?
You test which profile is available and depending on the profile, you set the valueChangedHandler.
It's important to realise that the extendedGamepad contains most functionality and the microGamepad least (I think the microGamepad is only used for the AppleTV remote). Therefore the checks should be ordered differently. An extendedGamepad has all functionality of the microGamepad + additional controls so in your code the method would always enter the microGamepad profile.
Apple uses the following code in the DemoBots example project:
private func registerMovementEvents() {
/// An analog movement handler for D-pads and movement thumbsticks.
let movementHandler: GCControllerDirectionPadValueChangedHandler = { [unowned self] _, xValue, yValue in
// Code to handle movement here ...
}
#if os(tvOS)
// `GCMicroGamepad` D-pad handler.
if let microGamepad = gameController.microGamepad {
// Allow the gamepad to handle transposing D-pad values when rotating the controller.
microGamepad.allowsRotation = true
microGamepad.dpad.valueChangedHandler = movementHandler
}
#endif
// `GCGamepad` D-pad handler.
// Will never enter here in case of AppleTV remote as the AppleTV remote is a microGamepad
if let gamepad = gameController.gamepad {
gamepad.dpad.valueChangedHandler = movementHandler
}
// `GCExtendedGamepad` left thumbstick.
if let extendedGamepad = gameController.extendedGamepad {
extendedGamepad.leftThumbstick.valueChangedHandler = movementHandler
}
}
I am working on a Unity Network game in which I have two players which have some basic moves. One player is controlled by the server and the other player is controlled by the client.
To accomplish this I have made a client/server connection. After the connection is made, I can see both players on both sides of the screen. I have used the RPC method.
Now if I make a move on the server, I can see the server player move on the client side as well. This means they are synchronized. But when I make a move on the client side, only the client player makes a move. I cannot see that move on the server side. Why doesn't this work?
I have written the code in UnityScript.
#pragma strict
var farword:boolean=false;
var backword:boolean=false;
var FirstPlayer:GameObject;
var SecondPlayer:GameObject;
var isFarword=false;
var isBackword=false;
function Update () {
if(isFarword)
{
networkView.RPC("ChangePos",RPCMode.All);
isFarword=false;
}
}
#RPC
function ChangePos()
{
if(isFarword)
{
if(Network.isServer)
{
FirstPlayer.transform.Translate(0,0,1);
isFarword=false;
}
if(Network.isClient)
{
SecondPlayer.transform.Translate(0,0,1);
isFarword=false;
}
}
else if(isBackword)
{
if(Network.isServer)
{
FirstPlayer.transform.Translate(0,0,-1);
isBackword=false;
}
if(Network.isClient)
{
SecondPlayer.transform.Translate(0,0,-1);
isBackword=false;
}
}
}
function OnGUI()
{
if(GUI.RepeatButton(new Rect(1000,100,80,70),"Farword"))
{
isFarword=true;
}
if(GUI.RepeatButton(new Rect(850,100,80,70),"second"))
{
isBackword=true;
}
}
The problem is probably where you instantiate the players..
Use the network events to create the players, eg..
Network.OnPlayerConnected, is where you should Network.Instantiate the client's player, and Network.OnServerInitialized is where you should Network.Instantiate the server's player. This will result in each of them being the owner.
Also, network games have some psycho logic to think through, try to always rather use NetworkView.isMine to determine who is the owner of the object and who isn't. Remember, only the owner is supposed to be able to move the object, the other players are listening in for coordinates.
Goodluck!