I am trying to make a very basic console game of rock paper scissors. I'm not sure how to go about using && in Swift 2.0. I want to basically say:
if (computerChoice == rock && userChoice == paper) {
print("User wins")
}
Well you appear to have it setup correctly. You could set this up using an enumeration variable.
enum RPSOption {
case Rock, Paper, Scissors
}
Then you just use it like so:
let computerChoice: RPSOption = RPSOption.Rock // or something else
let userChoice: RPSOption = RPSOption.Paper // or something else
if( computerChoice == .Rock && userChoice == .Paper ){
// do something
}
// do similar if statements for other conditions
Related
In Swift, there's a simple language feature to chain unwrapping and checking for non null like this:
if let data = functionReturningNullableNumber(), data != 0 { processNum(data) }
if let data = functionReturningNullableString(), data != "" { processStr(data) }
In Flutter, currently I have to do this:
var dataNum = functionReturningNullableNumber();
if (dataNum != null && dataNum != 0) { processNum(dataNum); }
var dataStr = functionReturningNullableString();
if (dataStr != null && dataStr != "") { processStr(dataStr); }
Is it possible to create something similar to the Swift above? Especially the part where I can assign a temporary variable name that will disappear outside of the if scope, so I can reuse the variable name.
Of course I can do this:
if (functionReturningNullableNumber() != null && functionReturningNullableNumber() != 0) { process(functionReturningNullableNumber()); }
But this isn't what I'm searching for.
I’m following a tutorial for SpriteKit that has a problem with an IF statement. The logic of the line is as follows: If the bullet and the asteroid collide then remove them.
if body1.categoryBitMask == PhysicsCategories.bullet && body2.categoryBitMask == PhysicsCategories.asteroid {
// remove bullet and asteroid
}
The problem arises when trying to make sure that the asteroid (body2.node) is inside the playable area before it can get shut down. For that, the author adds the following:
body2.node?.position.y < self.size.height
Making the complete IF statement as follows:
if body1.categoryBitMask == PhysicsCategories.bullet && body2.categoryBitMask == PhysicsCategories.asteroid && body2.node?.position.y < self.size.height {
// remove bullet and asteroid
}
Apparently that line works with Swift 2 however Swift 3 makes a correction changing the position from an optional and force unwraps the position.
if body1.categoryBitMask == PhysicsCategories.bullet && body2.categoryBitMask == PhysicsCategories.asteroid && body2.node!.position.y < self.size.height {
// remove bullet and asteroid
}
By force unwrapping the position, the app crashes “I THINK” when the three bodies collide. It is really difficult to tell when looking at the screen.
I’m testing the code below and I have not encounter any problems as of yet. Do you guys think that the fix below will work? What I'm thinking is, if I make sure the body2.node is not nil, then there is no reason why the app should crash since is not going to encounter a nil upon trying to force unwrap it.
if body1.categoryBitMask == PhysicsCategories.bullet && body2.categoryBitMask == PhysicsCategories.asteroid {
// If the bullet has hit the asteroid
if body2.node != nil {
if ( body2.node!.position.y < self.size.height ) {
// remove bullet and asteroid
}
}
}
Or else, if there another way you guys can suggest a different way to write the original IF Statement?
Thanks
Yes, the if != nil statement (as it is currently written) will protect against force-unwrap-induced crashes.
An alternative is to use the if let syntax in Swift:
if body1.categoryBitMask == PhysicsCategories.bullet && body2.categoryBitMask == PhysicsCategories.asteroid {
// If the bullet has hit the asteroid
if let body2Node = body2.node {
if body2Node.position.y < self.size.height {
// remove bullet and asteroid
}
}
}
The benefit is that it removes the ! from your code, and more clearly connects the nil check with the variable you are using later.
nathan has the right answer, but a better alternative would be to use a guard instead to protect your function:
...
guard let body1Node = body1.node, let body2Node = body2.node else {return}
//Beyond this point we need to guarentee both nodes exist
if body1.categoryBitMask == PhysicsCategories.bullet && body2.categoryBitMask == PhysicsCategories.asteroid {
// If the bullet has hit the asteroid
if body2Node.position.y < self.size.height {
// remove bullet and asteroid
}
}
By using a guard, we reduce nesting, and we know that beyond this point, the physics body must contain a node, unless we remove it before the function ends, which will eliminate any further need to check if the node exists.
As a side note, I always recommend to remove nodes at the end of your update phase to avoid issues like this, and instead just mark the node somehow that you will no longer be using it.
Following code was working well with swift 2 but it is not working with swift 3.
let constraints: NSArray = contentView.constraints as NSArray
let indexOfConstraint = constraints.indexOfObject (passingTest: { (constraint, idx, stop) in
return (constraint.firstItem ).tag == bubbleTag && (constraint.firstAttribute == NSLayoutAttribute.left || constraint.firstAttribute == NSLayoutAttribute.right)
})
I am getting following error:
Binary operator '==' cannot be applied to operands of type '_' and
'NSLayoutAttribute'
I am getting this error for following line:
constraint.firstAttribute == NSLayoutAttribute.left
How can i fix it?
That error message is showing only the first error that your code may cause, and you may need some more fixes to work on it.
Assuming you are using NSArray (or NSMutableArray) for constraints, why don't you declare constraints as [NSLayoutAttribute]?
With that change, you may need to fix some other parts, but the code getting the index can be written like this:
let indexOfConstraint = constraints.index {constraint in
return constraint.firstItem.tag == bubbleTag && (constraint.firstAttribute == NSLayoutAttribute.left || constraint.firstAttribute == NSLayoutAttribute.right)
}
//indexOfConstraint becomes `Int?`, so you may need to use Optional binding
if let index = indexOfConstraint {
//Do something for found case
} else {
//Do something for not-found case
}
If you cannot change the type of constraints globally, you can put this sort of code in your local code-block just before the code above.
guard let constraints = constraints as? [NSLayoutConstraint] else {
fatalError("something wrong with `constraints`")
}
Please, check below updated code:
let constraints: NSArray = contentView.constraints as NSArray
let indexOfConstraint = constraints.indexOfObject (passingTest:){ ( constraint, idx, stop) in
return ((constraint as AnyObject).firstItem as! UIView).tag == bubbleTag && ((constraint as AnyObject).firstAttribute == NSLayoutAttribute.left || (constraint as AnyObject).firstAttribute == NSLayoutAttribute.right)
}
This is my original function in Swift 2:
// check on winning combinations
func checkWinnerMove(){
for var i = 0; i<winningCombinations.count && !isWinner;i += 1 {
if gameState[winningCombinations[i][0]-1] == activePlayer &&
gameState[winningCombinations[i][1]-1] == activePlayer &&
gameState[winningCombinations[i][2]-1] == activePlayer{
isWinner = true;
}else{
isWinner = false;
}
}
}
I have changed it to this:
// check on winning combinations
func checkWinnerMove(){
for i in 0 ..< winningCombinations.count && !isWinner{
if gameState[winningCombinations[i][0]-1] == activePlayer &&
gameState[winningCombinations[i][1]-1] == activePlayer &&
gameState[winningCombinations[i][2]-1] == activePlayer{
isWinner = true;
}else{
isWinner = false;
}
}
}
But keep getting a error when I add the
&& !isWinner
statment in the for-in loop. The error I get is:
No '..<' candidates produce the expected contextual result type 'Bool'
Any suggestions? Thank You!
Instead of forcibly trying to rewrite your original C-style for loop, consider what you're trying to achieve and attempt to re-write it in "native" Swift from scratch. How about breaking out of your loop once your true condition is met, instead of keeping it in the loop signature? E.g.
for i in 1...5 {
print(i)
if i == 3 { break }
} // 1 2 3
Applied to your example
func checkWinnerMove()
isWinner = false
for i in 0 ..< winningCombinations.count {
if gameState[winningCombinations[i][0]-1] == activePlayer &&
gameState[winningCombinations[i][1]-1] == activePlayer &&
gameState[winningCombinations[i][2]-1] == activePlayer {
isWinner = true
break
}
}
}
The explicit by index access of the (unknown for us) gameState and winningCombinations sequences is quite "unswifty" w.r.t. in the dangers of runtime exceptions for indices out of range for the sequences. So bear in mind that there are safer ways to perform such access.
For future reference, consider reading How to create a Minimal, Complete, and Verifiable example: since we (the potential answerer's of your question) don't have access to/the full information regarding isWinner, winningCombinations, gameState or activePlayer, we can't verify your example. Also, since the question cover a concept, it could be boiled down to a more minimal form. (Welcome to StackOverflow!)
I'm working on a script that returns to the correct scene when pressing escape.
In the DataUpgrade, Level Select, TransferData(x) it should return to StartMenu, in StartMenu it should close the game and in a Level(x) it should return to TransferData(x).
The following script has been made with help from FunctionR.
#pragma strict
import System;
function Update () {
if (Input.GetKey ("escape"))
{
var level : String = Application.loadedLevel.ToString();
//var transferData = "TransferData";
if(level == "StartMenu"){
Application.Quit();
}
else if(level == "DataUpgrade"){
Application.LoadLevel("StartMenu");
}
else if(level == "Level Select"){
Application.LoadLevel("StartMenu");
}
else if(level.Contains("TransferData"))
Application.LoadLevel("StartMenu");
else if(level.Contains("Level"))
Application.LoadLevel (Application.loadedLevel - 1);
//trying this out but isn't working either
/*else if( transferData in level)
Application.LoadLevel("StartMenu");
else if("Level" in level)
Application.LoadLevel (Application.loadedLevel - 1);*/
}
}
i'm not sure why it's not working, the scenes have the correct names and have also been added to build settings.
Thanks in advance
Quick Fix
This is how you fix it, and you don't need the for loop. I use Contains() to do the hard work.
import System;
function Update ()
{
if (Input.GetKey ("escape"))
{
var level : String = Application.loadedLevel.ToString();
if(level == "StartMenu")
Application.Quit();
else if(level == "DataUpgrade")
Application.LoadLevel("StartMenu");
else if(level == "Level Select")
Application.LoadLevel("StartMenu");
else if(level.Contains("TransferData")
Application.LoadLevel("StartMenu");
}
}
Smart Fix
You can also be smart and take advantage of the OR operator to reduce your related if statements.
else if(level == "DataUpgrade" || level == "Level Select" || level.Contains("TransferData")
Application.LoadLevel("StartMenu");
Sneaky Fix
You can also be sneaky since all other cases, except one, takes you back to StartMenu. You can have an else that guarantees a return to StartMenu.
if(Application.loadedLevel == "StartMenu")
Application.Quit();
else
Application.LoadLevel("StartMenu");
Replacing this line:
var level : String = Application.loadedLevel.ToString();
with
var level = Application.loadedLevelName;`
fixed the problem, everything is working now