在这个简单的游戏中,有一个类的Fighter,其目的是使两个战士战斗。将健康值降低到0以下的人,就会失去游戏。
为了进行战斗,有一个静态方法战斗(..)反复进行,直到一名战斗机赢得游戏,并得到另一种非静态方法攻击(..)
对象战斗机的健康状况应该随着两个对象在游戏中使用Fight(...)和Attack(...)进行战斗而改变。问题在于它总是打印出相同的Fighter健康状况,并且游戏永远不会结束。我看不出问题出在哪里
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let david = Fighter(name: "David", health: 100, damage: 30, defense: 10, initiative: 80)
let goliath = Fighter(name: "Goliath", health: 300, damage: 60, defense: 14, initiative: 90)
let myFight1 = Fighter.fight(fighter1: david, fighter2: goliath) // always executing same Fighters
print(myFight1)
}
}
import Foundation
struct Fighter {
var name: String
var health: Double
var damage: Int
var defense: Int
var initiative: Int
init (name: String, health: Double, damage: Int, defense: Int, initiative: Int) {
self.name = name
self.health = health
self.damage = damage
self.defense = defense
self.initiative = initiative
}
init (name: String, health: Double, damage: Int, defense: Int) {
self.name = name
self.health = health
self.damage = damage
self.defense = defense
self.initiative = 0
}
static func fight(fighter1: Fighter, fighter2: Fighter) -> Fighter {
let f1 = fighter1
let f2 = fighter2
if f1.health == f2.health {
return f1
}
if f2.initiative > f1.initiative {
f2.attack(f: f1)
}
var i = 0
while f1.health > 0 {
i += 1
print("--> i: \(i)")
f1.attack(f: f2 )
if f2.health <= 0 {
return f1
}
f2.attack(f: f1)
}
return f2
}
func attack(f: Fighter) -> Void {
var g = f
g.health = g.health - Double(g.damage * (1 - g.defense / 100))
print(g)
}
}
答案 0 :(得分:3)
您正在将struct
的{{1}}用于Swift中的一种值类型。
值类型最基本的区别在于复制-赋值,初始化和参数传递的效果- 创建一个具有其数据唯一副本的独立实例
解决方案:将Fighter
更改为Fighter
,您就可以了。
输出的打印语句:(第二个打印语句已更改为class
)
大卫70.0
->我:1
巨人240.0
大卫40.0
->我:2
巨人180.0
大卫10.0
->我:3
巨人120.0
大卫-20.0
要了解更多信息,请访问:Value and Reference Types
答案 1 :(得分:1)
每次调用方法func attack(f: Fighter) -> Void
之后,被攻击的Fighter
的属性不会得到更新。因此while
循环在任何时候都不会中断。
请替换下面的代码。
static func fight(fighter1: Fighter, fighter2: Fighter) -> Fighter {
var f1 = fighter1
var f2 = fighter2
if f1.health == f2.health {
return f1
}
if f2.initiative > f1.initiative {
f1 = f2.attack(f: f1)
}
var i = 0
while f1.health > 0 {
i += 1
print("--> i: \(i)")
f2 = f1.attack(f: f2 )
if f2.health <= 0 {
return f1
}
f1 = f2.attack(f: f1)
}
return f2
}
func attack( f: Fighter) -> Fighter {
var g = f
g.health = g.health - Double(g.damage * (1 - g.defense / 100))
print(g)
return g
}
答案 2 :(得分:1)
当你说...
var g = f
...实际上是在创建该对象的副本,而不是引用。因此,当您更改“健康”属性时,可以在副本中进行更改。 有两种简单的解决方案:
1)将结构更改为类,因为正在引用类,这与仅复制结构的结构不同。
2)用修改后的副本(g)替换原始对象
答案 3 :(得分:0)
正如Rakesha所指出的那样,结构是值类型,因此您的attack
代码实际上并未进行任何修改:
func attack(f: Fighter) -> Void {
var g = f // Make a mutable copy of `f` called `g`
g.health = g.health - Double(g.damage * (1 - g.defense / 100)) // Modify g
print(g) // Print g
// Throw g away
}
(旁注:我认为g.damage
在这里不正确;我认为您可能是指self.damage
。)
那里实际上什么都没有修改f
。有几种解决方法。一种是使用类,它引入了微妙的可变状态。我认为我不会在这里这样做。 “微妙的可变状态”是指您期望attack
修改f
,但是签名中没有任何内容表明这样做了,因此调用者可能会感到惊讶。
相反,您有几种方法可以实现此目的,从而使您的突变在结构上明确。您可以使attack
显式修改f
:
func attack(f: inout Fighter) {
f.health = f.health - Double(damage * (1 - f.defense / 100))
}
或者您也可以在受到其他人的攻击时扭转它并进行修改:
mutating func attackedBy(f: Fighter) {
health = health - Double(f.damage * (1 - defense / 100)
}