protected override void Awake() { base.Awake(); var controller = GetComponent <DatingController>(); controller.judgements.ObserveAdd() .Subscribe(judgement => { var profile = controller.currentProfileView.data; AnalyticsEvent.Custom("swipe", new Dictionary <string, object>() { { "on_profile", profile.name }, { "matched", judgement.Value.judgement == DatingProfileJudgement.Matched }, { "order", judgement.Index } }); var eventHitBuilder = new EventHitBuilder() .SetEventCategory("dating") .SetEventAction("swipe") .SetEventLabel(profile.name) .SetEventValue(judgement.Value.judgement == DatingProfileJudgement.Matched ? 1L : 0L) .SetCustomMetric(0, judgement.Index); GoogleAnalyticsV4.getInstance().LogEvent(eventHitBuilder); }) .AddTo(this); }
private void OnDialogueEnded(DialogueItem dialogue, CompositeDisposable subscriptions) { subscriptions.Dispose(); dialogue.ended = true; m_ChatBoxController.HideOptions(); m_ConversationController.StopTyping(); m_DialogueEnded.OnNext(new DialogEndedEvent { item = selectedDialogue.Value, profile = profile.Value, succeeded = dialogue.succeeded }); m_ExitButton.SetActive(true); m_ChatBoxController.gameObject.SetActive(false); AnalyticsEvent.LevelComplete(profile.Value.name, new Dictionary <string, object>() { { "succeeded", dialogue.succeeded } }); var eventHitBuilder1 = new EventHitBuilder() .SetEventCategory("conversation") .SetEventAction("ended") .SetEventLabel(profile.Value.name) .SetEventValue(dialogue.succeeded ? 1L : 0L); GoogleAnalyticsV4.getInstance().LogEvent(eventHitBuilder1); }
protected override void Awake() { base.Awake(); Debug.Log($"DialogueConversationController: Loaded {m_Sprites.Length} sprite(s)."); // When a text phase event is processed from the dialogue system and it didn't have choices, this will post // a delayed dialogue continuation to make it look like the other person was typing. Posts the length // of the output for the purposes of making an appropriate delay. var lastChoice = -1; CompositeDisposable subscriptions = null; m_ChatBoxController.choices .Subscribe(choice => { lastChoice = choice.index; }) .AddTo(this); profile .Where(item => item != null) .Subscribe(item => { m_DatingProfileView.data = item; m_ConversationController.conversantName = profile.Value.name; }) .AddTo(this); selectedDialogue .StartWith((DialogueItem)null) .Pairwise() .Subscribe(dialogues => { var oldDialogue = dialogues.Previous; var newDialogue = dialogues.Current; if (oldDialogue == newDialogue || newDialogue == null) { return; } subscriptions?.Dispose(); subscriptions = ResumeStory(newDialogue, oldDialogue); AnalyticsEvent.Custom("chat_with", new Dictionary <string, object>() { { "on_profile", profile.Value?.name } }); var eventHitBuilder1 = new EventHitBuilder() .SetEventCategory("conversation") .SetEventAction("chat_with") .SetEventLabel(profile.Value?.name); GoogleAnalyticsV4.getInstance().LogEvent(eventHitBuilder1); }) .AddTo(this); }
/// <summary> /// Undoes the last stamping action /// </summary> public void Undo() { drawingView.stampItems.RemoveAt(drawingView.stampItems.Count - 1); // Analytics var hit = new EventHitBuilder() .SetEventCategory("profile creator") .SetEventAction("undo") .SetEventValue(drawingView.stampItems.Count); GoogleAnalyticsV4.getInstance().LogEvent(hit); m_CanUndo.Value = drawingView.stampItems.Count > 0; }
protected override void Awake() { base.Awake(); var screenView = GetComponent <ScreenView>(); screenView.onScreenBeginTransition.AsObservable() .Subscribe(next => { var nextScreen = screenView.materialScreen[next]; AnalyticsEvent.ScreenVisit(nextScreen.gameObject.name); GoogleAnalyticsV4.getInstance().LogScreen(nextScreen.gameObject.name); }) .AddTo(this); }
private void SendGaHitWithMeasurementProtocol(string url) { if (String.IsNullOrEmpty(url)) { if (GoogleAnalyticsV4.belowThreshold(logLevel, GoogleAnalyticsV4.DebugMode.WARNING)) { Debug.Log("No tracking code set for 'Other' platforms - hit will not be sent."); } return; } if (dryRun || optOut) { if (GoogleAnalyticsV4.belowThreshold(logLevel, GoogleAnalyticsV4.DebugMode.WARNING)) { Debug.Log("Dry run or opt out enabled - hits will not be sent."); } return; } if (startSessionOnNextHit) { url += AddOptionalMPParameter(Fields.SESSION_CONTROL, "start"); startSessionOnNextHit = false; } else if (endSessionOnNextHit) { url += AddOptionalMPParameter(Fields.SESSION_CONTROL, "end"); endSessionOnNextHit = false; } // Add random z to avoid caching string newUrl = url + "&z=" + UnityEngine.Random.Range(0, 500); if (GoogleAnalyticsV4.belowThreshold(logLevel, GoogleAnalyticsV4.DebugMode.VERBOSE)) { Debug.Log(newUrl); } GoogleAnalyticsV4.getInstance().StartCoroutine(this.HandleWWW(new WWW(newUrl))); }
// Start is called before the first frame update protected override void Start() { m_CategoryPagerView.items.Value = m_Categories; var categoryObservable = category as IObservable <CategoryEnum>; var isDragAndDropping = new BoolReactiveProperty(false); // If we have at least one category, start with it if (m_Categories.Length > 0) { categoryObservable = categoryObservable.StartWith(m_Categories[0].category); } // Change the catalogue pager whenever the category button is pressed or the color changes. Observable.Merge( categoryObservable .DistinctUntilChanged() .AsUnitObservable(), flipped.DistinctUntilChanged() .AsUnitObservable(), color.DistinctUntilChanged() .AsUnitObservable() ) .Subscribe(ignored => { var catalogueItems = m_Asset[category.Value, color.Value] .Select(item => new CatalogueItem() { flipped = flipped.Value, asset = item }) .ToArray(); m_CataloguePagerView.items.Value = catalogueItems; if (m_Brush.Value != null) { var index = Array.FindIndex(catalogueItems, item => item.asset.name == m_Brush.Value.catalogueItem.asset.name); if (index >= 0) { // If the color or flip state changed, it now changed here var brush = new BrushItem() { catalogueItem = catalogueItems[index] }; m_Brush.SetValueAndForceNotify(brush); } } var hit = new EventHitBuilder() .SetEventCategory("profile creator") .SetEventAction("brush") .SetEventLabel($"category={category.Value}, color={color.Value}, flipped={flipped.Value}") .SetEventValue(drawingView.stampItems.Count); GoogleAnalyticsV4.getInstance().LogEvent(hit); }) .AddTo(this); category.Value = m_Categories[0].category; // Whenever we click on a catalogue item, set that image as the current brush, then stamp its pixels onto // the drawing image with our brush when it is clicked m_CataloguePagerView.OnCatalogueItemPointerClickAsObservable() .Subscribe(tuple => { // If we clicked instead of dragged the item, we're definitely not dragging isDragAndDropping.Value = false; brush.Value = new BrushItem() { catalogueItem = tuple.Item2 }; }) .AddTo(this); // If we switch into the screen view that contains the editing profile text, copy in the image to it m_UiScreenView.onScreenBeginTransition.AsObservable() .Where(targetScreen => targetScreen == m_EditingProfileTextScreen.screenIndex) .Subscribe(ignored => { Drawing.PostDrawing(drawingView, m_EditingProfileTextDrawingView); }) .AddTo(this); // Drawing code var opaque = new Color(1f, 1f, 1f, 1f); var transparent = new Color(1f, 1f, 1f, m_PreviewBrushTransparency); // Draw a preview of the brush var previewBrush = new GameObject("Preview Brush"); var previewImage = previewBrush.AddComponent <Image>(); // The preview image brush naturally should not receive raycasts / should not block previewImage.raycastTarget = false; var previewBrushTransform = previewBrush.transform as RectTransform; Debug.Assert(previewBrushTransform != null, nameof(previewBrushTransform) + " != null"); // Changes the preview's sprite as long as the brush is not null. The brush is actually set inactive later. // Also reacts to when the flipping setting changes. brush .Where(b => brush.Value != null && brush.Value.sprite != null) .Subscribe(b => { previewImage.sprite = b.sprite; previewImage.rectTransform.localScale = new Vector3(b.flipped ? -1.0f : 1f, 1f, 1f); previewImage.SetNativeSize(); }) .AddTo(this); // Move the preview brush along with the mouse as long as there is a brush selected // Whether or not it will be visible depends on which layer it's on and whether or not we're on a mobile // device. Mobile devices do not show the preview when the item was just clicked. The layer is changed later. Observable.EveryUpdate() .Where(ignored => isActiveAndEnabled) .Subscribe(ignored => { if (brush.Value == null || Application.isMobilePlatform && !isDragAndDropping.Value) { previewBrush.SetActive(false); return; } previewBrush.SetActive(true); var pointer = ContinuousStandaloneInputModule.instance.pointerEventData; var isOverDrawingView = pointer.pointerCurrentRaycast.gameObject == m_DrawingView.raycastTarget.gameObject; previewImage.color = isDragAndDropping.Value ? isOverDrawingView ? opaque : transparent : opaque; previewBrushTransform.transform.position = BrushPosition(pointer); }) .AddTo(this); // When we are drag and dropping, put the preview brush on the root canvas layer so that its totally in // the foreground. Otherwise, put it in the preview layer so it only appears when the user's pointer is // hovering over the drawing canvas isDragAndDropping.StartWith(false) .Subscribe(dragging => { previewBrushTransform.SetParent(dragging ? m_ScreenRectTransform : m_DrawingView.previewLayer); }) .AddTo(this); // Draw the brush when it is stamped var foreground = m_DrawingView.stampLayer; m_DrawingView.OnPointerClickAsObservable() .Where(ignored => brush.Value != null) .Subscribe(pointer => { Stamp(pointer, foreground, brush.Value.flipped); }) .AddTo(this); // Enable drag and dropping a specific sprite. Temporarily switches the brush and activates drag and drop mode m_CataloguePagerView .OnCatalogueItemBeginDragAsObservable() .Subscribe(tuple => { isDragAndDropping.Value = true; brush.Value = new BrushItem() { catalogueItem = tuple.Item2 }; }) .AddTo(this); // Stamp when we stop dragging a catalogue item m_CataloguePagerView .OnCatalogueItemEndDragAsObservable() .Subscribe(tuple => { isDragAndDropping.Value = false; // If we're over the drawing view, we should stamp the sprite var isOverDrawingView = tuple.Item1.pointerCurrentRaycast.gameObject == m_DrawingView.raycastTarget.gameObject; if (isOverDrawingView) { Stamp(tuple.Item1, m_DrawingView.stampLayer, tuple.Item2.flipped); } // Then clear the brush no matter what brush.Value = null; }) .AddTo(this); // Toggle the flip automatically if this particular asset is configured to do so stampEvents.Where(stamp => stamp != null && stamp.brush.flipsAfterStamp) .Subscribe(stamp => { flipped.Value = !flipped.Value; }) .AddTo(this); drawingView.stampItems.ObserveCountChanged(true) .Subscribe(count => { m_CanUndo.Value = count > 0; }) .AddTo(this); // Save as the user stamps stampEvents .Subscribe(stampEvent => { // This throttles internally SaveGameController.instance.Save(); if (stampEvent != null) { // Analytics var hit = new EventHitBuilder() .SetEventCategory("profile creator") .SetEventAction("stamp") .SetEventLabel(stampEvent.brush.sprite.name) .SetEventValue(drawingView.stampItems.Count); GoogleAnalyticsV4.getInstance().LogEvent(hit); } }) .AddTo(this); }
private void OnNextStory(DialogueItem dialogue, Subject <DialogueEvent> events, Choice choice = null, string overrideMessage = null, bool silent = false) { var story = dialogue.story; var self = false; var noDelay = false; var noBubble = false; var success = false; if (choice != null) { var substring = choice.text?.Substring(0, Mathf.Min(choice.text?.Length ?? 0, 8)); AnalyticsEvent.Custom("choice", new Dictionary <string, object>() { { "path", story.state?.currentPathString }, { "profile", profile.Value?.name }, { "text", substring }, { "index", choice.index } }); var eventHitBuilder1 = new EventHitBuilder() .SetEventCategory("conversation") .SetEventAction("choice") .SetEventLabel(choice.text) .SetCustomDimension(0, story.state?.currentPathString) .SetCustomDimension(1, profile.Value?.name); GoogleAnalyticsV4.getInstance().LogEvent(eventHitBuilder1); story.ChooseChoiceIndex(choice.index); self = true; noDelay = true; } var eventHitBuilder2 = new EventHitBuilder() .SetEventCategory("conversation") .SetEventAction("position") .SetEventLabel(story.currentText) .SetCustomDimension(0, story.state?.currentPathString) .SetCustomDimension(1, profile.Value?.name); GoogleAnalyticsV4.getInstance().LogEvent(eventHitBuilder2); if (story.canContinue) { var message = story.Continue(); success = story.currentTags.Any(s => s.ToLower() == DialogueConversationController.success); noBubble = story.currentTags.Any(s => s.ToLower() == DialogueConversationController.noBubble); self |= story.currentTags.Any(s => s.ToLower() == DialogueConversationController.self); silent |= story.currentTags.Any(s => s.ToLower() == DialogueConversationController.silent); if (success) { dialogue.succeeded = true; } // Remove starting and ending quotation marks, extraneous line returns message = message.Trim('\n', '"'); if (message.StartsWith(m_StartOfSelfConversation)) { self |= true; message = message.Substring(1); } // If this is the last message of the dialogue, no delay if (!story.canContinue && (story.currentChoices?.Count ?? 0) == 0) { noDelay = true; } var dialogueEvent = new DialogueEvent { text = overrideMessage ?? message, noBubble = noBubble, choices = story.currentChoices, self = self, noDelay = noDelay, success = success, silent = silent }; dialogue.lastEvent = dialogueEvent; events.OnNext(dialogueEvent); } else { var dialogueEvent = new DialogueEvent { finished = true, success = dialogue.succeeded }; dialogue.lastEvent = dialogueEvent; events.OnNext(dialogueEvent); } }
private void OnDialogueEvent(DialogueItem dialogue, DialogueEvent data, Subject <DialogueEvent> events, Subject <DialogueEvent> dialogueContinue) { // Handle the function call for riddles here. This flag prevents also fake choices used to implement // the "Future<int>" that we're doing here from appearing inside the chat box. var story = dialogue.story; var dataRiddle = data.riddle; if (dataRiddle != null) { dialogue.suppressChoices = true; m_ChatBoxController.RequestInput(res => { #if UNITY_WEBGL // ReSharper disable once SpecifyStringComparison var answerWasCorrect = res.Trim().ToLower() == dataRiddle.Trim().ToLower(); #else var answerWasCorrect = string.Equals(res, riddle, StringComparison.CurrentCultureIgnoreCase); #endif AnalyticsEvent.Custom("riddle_input", new Dictionary <string, object>() { { "answer", res } }); var eventHitBuilder1 = new EventHitBuilder() .SetEventCategory("conversation") .SetEventAction("riddle_input") .SetEventLabel(res); GoogleAnalyticsV4.getInstance().LogEvent(eventHitBuilder1); OnNextStory(dialogue, events, story.currentChoices[answerWasCorrect ? 1 : 0], overrideMessage: res); }); } else if (data.text != null && !data.silent) { var self = data.self; var text = data.text; // Render images if one was specified var spriteName = ParseSpriteName(text); // Retrieves all the sprites that have been referenced SOMEWHERE in the scene. See m_Sprites on this // instance and add the sprites to make sure the image can render in the chat. var sprite = spriteName == null ? null : Drawing.sprites[spriteName]; m_ConversationController.Add(new[] { new ChatMessage { noBubble = data.noBubble, message = spriteName != null ? "" : data.text, image = sprite, self = self } }); } // This is definitely no longer the first message. dialogue.first = false; // Show the choices if there are any to show AND we're not awaiting an input if (data.choices?.Count == 0 && dataRiddle == null) { dialogueContinue?.OnNext(data); } else if (data.choices?.Count > 0 && dataRiddle == null) { m_ChatBoxController.responses.Clear(); if (dialogue.suppressChoices) { dialogue.suppressChoices = false; } else { foreach (var choice in data.choices) { m_ChatBoxController.responses.Add(choice.text); } } } }