Keeping SKNodes upright with GameplayKit - sprite-kit

I'm working on a SpriteKit game with animal characters. The animals move left and right across the screen using agents from GameplayKit. They follow an invisible agent that goes from one side of the screen to the other. This works ok except that the sprites are moving upside down when I go from right to left (they're ok when they go from left to right). I've tried changing the xScale to get a mirror image but it looks like the agent is overriding the SKAction I've made to change this (the characters are jumpy on the screen for an instant). Does anyone know how to get the sprite characters to remain upright? My code is triggered in func didBeginContact.
if contact1 is RightWall {
if contact2 is AnimalNode {
invisibleNode.position = CGPointMake(0, CGFloat(10 + arc4random_uniform(UInt32(size.height - 20))))
invisibleNode.agent.position = float2(x: Float(invisibleNode.position.x), y: Float(invisibleNode.position.y))
contact2.turnLeft() // this triggers the xScale change I've tried directly on the SKNode
}
Any ideas how to fix this?
Update: the code is shown in the original question.
The moving node is prepared in a child of SKNode (class: AnimalNode) with the following function:
override func turnLeft() {
removeActionForKey("rightTurn") let turnRight2Left = SKAction.sequence([SKAction.scaleXTo(0.3, duration: 0.1), SKAction.scaleXTo(-0.3, duration: 0.1), SKAction.scaleXTo(-1.0, duration: 0.1)]) runAction(turnRight2Left, withKey: "leftTurn")
}
RightWall is an SKShapeNode set up in GameScene. The contact works ok as I added a print statement to check if the if statements are working. The call to func turnLeft also shows no problems, just nothing changes in the node. Maybe this isn't the best way to flip the character. I'm open to other approaches. The turnLeft func is set-up to make the change in character's orientation look more seamless when it transitions from moving right to moving left (that's why I change the sprite's xScale in stages to give it a more 'animated' feel).

thank you both for your help. It took some digging but I found another call to xScale which I was subclassed to. This was over-riding my call and causing the jumping action I'd noticed. I can combine the parent method and solve the problem. Thanks very much for the tips and feedback.

Related

Can QML drag-and-drop mechanic work without drag item moving?

I have a listview and a rectangle on top of it. The ListView's delegates have DropAreas in them and the rectangle has drag enabled on Y axis with its relevant properties (hotspot, target).
When I move the rectangle up and down on the ListView, the DropAreas of the delegates are registering onEntered signals. Working as intended.
However, I am using SmoothedAnimation to scroll the list up and down when the rectangle is at the most top and bottom coordinates (list.y = 0 and list.height). So the rectangle is not moving, and the DropAreas of the list's delegates are moving under it. In that case, the onEntered is not registered because there is no dragging, the rectangle is completely still and although its hotspot is entering and leaving the DropAreas, there is no interaction.
This is because dragging mechanic is sending events all the time when moving and any DropAreas it comes inside can register the event. In my case there is no dragging and therefore no events.
Question: Can drag events be manually activated? Can I somehow simulate drag?
At first, you should change the drag.target: parent to drag.target: this. In this way instead of dragging the parent item, you drag only the mouseArea. After that, you should grab an image from the item that you want to drag. The code is here:
Rectangle {
id: rec
width: 100
height: 100
Drag.active: dragArea.drag.active
Drag.dragType: Drag.Automatic
Drag.supportedActions: Qt.CopyAction
MouseArea {
id: dragArea
anchors.fill: parent
drag.target: this
onPressed:{
parent.grabToImage(function(result) {
parent.Drag.imageSource = result.url
})
}
}
}
I have managed to induce drag events by manually changing the hotSpot property of the rectangle above the list. QML Drag documentation says:
Changes to hotSpot trigger a new drag move with the updated position.
So I have done this every time Listview contentY changes (vertical scrolling happens)
onContentYChanged:
{
rectangle.Drag.hotSpot.x += 0.0001
rectangle.Drag.hotSpot.x -= 0.0001
}
In hindsight, however, this is a 'hacky' solution. hotSpot property is a QPointF class property (link). This means it can be set using various methods (setX, setY, rx, ry..). For example, this:
rectangle.hotSpot += Qt.point(0,0)
or
rectangle.hotSpot = Qt.point(rectangle.hotSpot.x,rectangle.hotSpot.y)
should work in theory by resetting the hotSpot point when contentY changes, but testing revealed it unfortunately does not trigger a new drag move. (bug or intended, I don't know)
Now some of you that are more into Qt and QML might know more about this and how to properly adress the issue, but the above solution works for me and after testing everything I could imagine to get a cleaner solution, I settled on it.

Changing the players sprite with animator

i have a 2d platformer with a player. The players default sprite is set to idle. There is 4 sprites and states in total --> idle, jumping, left, right.
Im trying to get the player to switch sprites depending on what their doing eg jumping or moving right.
This is my first time using unity and animator so im not experienced at all.
So each of my animations look something like this. 1 frame and one for each state:
And then I open up the animator:
Im not too sure what the arrows do but i figured its like a flow chart so you go from idle to left so i matched up the arrows. I then also made 4 booleans for each state.
Then on the script attached to the player, i added this code:
private Animator animator;
void Start () {
animator = GetComponent<Animator>();
}
//This is in the update function
if (!playerLeft && !playerRight && !jumping)
{
setAnimationState("playerIdle");
}
else if (jumping)
{
setAnimationState("playerJumping");
}
}
private void setAnimationState(String state){
animator.SetBool("playerJumping", false);
animator.SetBool("playerLeft", false);
animator.SetBool("playerRight", false);
animator.SetBool("playerIdle", false);
animator.SetBool(state, true);
}
So i made a function that cancels of the animations and switches it to the correct one which is called. You can see how i call it with jumping and i also have the same for left and right but i know they are all being called correctly from the animator.
In the animator, only the right grey box is running. I can see because it has a blue bar and its not switching animations. The booleans are switching but thats it.
On the top of my head:
You need a transition from right to left and left to right.
You need one from right, left and jumping back to idle.
You can click on the arrows and select which booleans need to be checked to enable the transition.
Fore each animation state you need to upload a spritesheet. You can drag and drop this in the animation tab. The animation will play untill there are no more sprites. It will then check if it can transition into another state or loop the current state. (Do note that you need to enable looping on each state, except jumping i guess?)
Welcome to Unity development :)
I don't want to make this too long, so let's just use playerLeft as an example.
So you've set the Idle state as the default state - meaning if all the booleans are false, the player is idle. But once you press the left key, you would want the player to transition from the idle state to the Left state.
First step is to click on the transition (the arrow) from idle to playerLeft.
Next, is to add the playerLeft boolean as the condition. This means that when the playerLeft boolean becomes true, we start transitioning from Idle to Left.
And finally, tinker with the code. We need to tell Unity to set playerLeft to true when we press the "D" button for example.
Update()
{
if(Input.GetKeyDown(KeyCode.D))
animator.SetBool("playerLeft",true);
}
We place it in the Update() method to make sure it runs every frame. Then, we have a condition where it waits for the player to press D.
There's still a whole lot to do after this such as doing return transitions but well, you'll figure out the rest as you go so don't hesitate to ask ;)

How can i get the gaze raycast to pass through image but also activate gaze event

I have two canvasses. One is nested in the other. They both express a behavior when looked at. While looking at the outer canvas it works fine, but when I look at the inner canvas, the behavior of the outer canvas stops.
I pinned it down to my raycast not coming through the inner canvas.
Can someone help me or link me to a know solution?
I've tried layers but I don't quite understand them. I hope for an easier solution.
To be specific. the outer canvas closes when focus is lost, so I try to keep the object focused. When I enter the inner canvas it doesn't register it as a hit on the outer one and it starts to close.
I work specifically with HololensToolkit. It handles most of the gaze events but there doesn't seem to be an option to get multiple raycasts.
use RaycastAll, and work with the array of returned objects instead.
for example:
RaycastHit[] hits;
hits = Physics.RaycastAll(transform.position, transform.forward, 100.0F);
foreach (RaycastHit hit in hits)
{
if (hit.gameObject.tag == "menuobj")
//do something
}

Stop and remove a SKScene from a View

a have subclassed a SKScene and implemented didMoveToView which did nothing at the moment. Empty function body. Let's name the class TestScene for a moment.
I also have a WindowController and a var named testScene:
var testScene: TestScene?
To functions are implemented showand hide
The show function looks like this:
func show(){
testScene = TestScene(fileNamed:"TestScene")
}
fileNamed:"TestScene" is of course the .sks file with a animation.
The hide function looks like this:
func hide(){
if let scene = testScene {
scene.paused = true
}
testScene?.removeFromParent()
testScene = nil
self.close()
}
This SpriteKit scene does appear on circumstances (button click) in my application. And after the execution is disappears.
Everything is working fine so far.
If I'll press the button the animation comes into the view and close after hide() is triggered, BUT: Since the button is pressed and
testScene = TestScene(fileNamed:"TestScene")
is executed, i got 0.5% CPU load. Even after hide. I can in commend the TestScene() and the CPU falls down to 0% after a little peak.
( i do some more stuff in that buttonPressed: function). So I am pretty sure that the TestScene is not removed after hide is called and maybe do some crazy shit in the background.
The Problem is, that the whole FinalScene will produce 5% of CPU and after the first appearance this load won't go away. :(
My Question is: How to remove the SKScene from the execution loop?
Thanks a lot for any advice, i debug this since hours by now and can't figure out how to dismiss the ended animation.
ps.
The solution is to explizit remove the SKView-subview:
animationView.removeFromSuperview()
This means that we have to build the view from the xib on every event, because the view is lost. The other possibility is to add the SKView programatically on show().

Why will my label not change location when trying to add additional text field?

So I have a button on my storyboard. When that button is pressed, I want to move a certain label down a little bit and also add a new text field to the screen. Here is the simplified code:
UIView.animateWithDuration(0.5, animations: { () -> Void in
self.addIngredientLabel.center.y += self.addIngredientLabel.bounds.height
}) { (_) -> Void in
let newIngredientTextField = UITextField(frame: CGRectMake(100, 110, 100, 100))
newIngredientTextField.center.y -= 50
newIngredientTextField.placeholder = "place text here"
newIngredientTextField.contentHorizontalAlignment = .Left
self.view.addSubview(newIngredientTextField)
}
The problem though, is that after the animation is completed, the label jumps back to its original location. It is like the animation is being pre-maturely terminated. The textField gets added and the label moves but it does not stay moved.
I have tried adding the text field at the end of the function instead of in a completion block. No kind of reordering seems to be helping.
I feel like I am not understanding something fundamental to animations. If anyone has any advice I would greatly appreciate it.
Since you're using the storyboard, you are most likely also using auto-layout. The positions of your controls are determined in your visual design (storyboard) and will adjust automatically to orientation changes and adapt to various device screen sizes. This is good but the flip size is that you cannot directly change the positions of your control using their x,y coordinates.
Instead, you can change values in the constraints you defined in the storyboard. for example, if you button has a "Top" distancs constraint with a control above it (or the edge of the view), you can change the constraint's "constant" attribute to indirectly change the position of the control.
In order to manipulate the contraints in your code, you can create IBOutlets for them just as you would do with any control.