24-04-23 Kotlin 간단한 텍스트 rpg 만들어보기 (2)
오늘도 코틀린 학습을 이어나갔다. 오늘은 헷갈리는 부분인 객체지향 위주로 공부하였다.
객체지향 부분에 간단한 과제가 있어서 그 부분을 정리해보았다.
1. 간단한 텍스트 rpg 만들어보기
1-1. 상속을 이용한 과제
[문제]
상속을 통해 방어구를 가진 캐릭터 클래스를 만들어보세요
- 캐릭터 클래스는 공격과 공격 당하기를 할 수 있어요
1. 공격을 하면 상대방에게 공격할 데미지를 전달해요
2. 공격을 당하면 데미지만큼 자신의 체력이 감소해요
- 방어구를 가진 캐릭터 클래스는 공격과 방어를 할 수 있어요
1. 공격을 하면 상대방에게 공격할 데미지를 전달해요
2. 공격을 당하면 데미지에서 방어력을 제한만큼 자신의 체력이 감소해요
[풀이]
먼저 캐릭터 클래스를 만들어 주었다. 그 다음 생성자를 먼저 정의해주었다.
아무것도 입력하지 않으면 기본 체력은 100, 공격력은 10이 들어간다. 체력과 공격력을 직접 넣어주려면 둘 다 입력해야한다.
그 다음 다른 캐릭터를 공격했을때를 나타내는 attack() 함수를 만들었다. 캐릭터가 가진 공격력으로 공격을 하게 된다.
그 다음 공격 받음을 나타내는 함수 attacked()를 만들어주었다. 받은 공격의 공격력만큼 체력이 줄어들게 된다.
그런 다음 캐릭터 클래스를 상속하는 방어구를 가진 캐릭터 클래스를 만들었다.
역시 생성자부터 정의해주었다. 방어구의 방어력을 입력하지 않으면 기본 방어력은 5이다.
아무것도 입력하지 않으면 체력, 공격력, 방어구의 방어력이 기본값이 들어간다.
방어구의 방어력만 입력하면 체력과 공격력이 기본값이 들어간다.
체력과 공격력만 입력하면 방어구의 방어력은 기본값이 들어간다.
체력, 공격력, 방어구의 방어력 모두 직접 입력할 수도 있다.
그런다음 공격받았음을 알려주는 함수 attacked()를 오버라이드해 체력이 공격한 상대의 공격력에서 방어구의 방어력만큼 덜 줄어들도록 코드를 완성했다.
아래가 완성한 코드이다.
[작성한 코드]
fun main() {
val character1 = Character()
val character2 = Character(200, 30)
val characterWithArmor1 = CharacterWithArmor(200, 30)
val characterWithArmor2 = CharacterWithArmor(300, 40, 10)
// '캐릭터1'이 '캐릭터2'를 공격한 뒤 '캐릭터2'의 체력
character1.attack(character2)
println(character2.health) // 190
// '캐릭터2'가 '방어구가 있는 캐릭터1'을 공격한 뒤 '방어구가 있는 캐릭터1'의 체력
character2.attack(characterWithArmor1)
println(characterWithArmor1.health) // 175
// '방어구가 있는 캐릭터1'이 '캐릭터1'을 공격한 뒤 '캐릭터1'의 체력
characterWithArmor1.attack(character1)
println(character1.health) // 70
// '캐릭터2'가 '방어구가 있는 캐릭터2'를 공격한 뒤 '방어구가 있는 캐릭터2'의 체력
character2.attack(characterWithArmor2)
println(characterWithArmor2.health) // 280
}
open class Character {
var health = 100
var power = 10
constructor()
constructor(_health: Int, _power: Int) {
health = _health
power = _power
}
fun attack(target:Character) {
target.attacked(power)
}
open fun attacked(damage:Int) {
health -= damage
}
}
class CharacterWithArmor:Character {
var shield = 5
constructor(): super()
constructor(_health: Int, _power: Int): super(_health, _power)
constructor(_health: Int, _power: Int, _shield: Int): super(_health, _power) {
shield = _shield
}
override fun attacked(damage:Int) {
health -= damage - shield
}
}
1-2. 조합을 이용한 과제
[문제]
조합을 통해 방어구를 가진 캐릭터 클래스를 만들어보세요.
- 캐릭터 클래스는 공격과 공격 당하기를 할 수 있어요
1. 공격을 하면 상대방에게 공격할 데미지를 전달해요
2. 공격을 당하면 데미지만큼 자신의 체력이 감소해요
- 방어구를 가진 캐릭터 클래스는 공격과 방어를 할 수 있어요
1. 공격을 하면 상대방에게 공격할 데미지를 전달해요
2. 공격을 당하면 데미지에서 방어력을 제한만큼 자신의 체력이 감소해요
[풀이]
생성자를 여러개 정의했더니 헷갈리고 다소 시간이 걸렸지만 정리를 잘 하니 모든 문제가 해결되었다.
1-1. 에서는 주생성자를 이용하지 않아 다소 복잡하게 코드를 작성했었는데 이번에는 주생성자와 보조생성자를 이용해서 코드를 작성해보았다.
먼저 아무것도 입력하지 않고 캐릭터 객체를 생성하면 체력 100, 공격력 10 그리고 방어구가 없는 캐릭터가 만들어진다.
체력과 공격력을 직접 입력해주면 체력과 공격력이 그 입력값을 가진 방어구가 없는 캐릭터가 만들어진다.
방어구의 경우도 방어력의 기본값은 5이고, 직접 방어력을 입력해줄 수도 있다.
캐릭터 객체를 생성할 때 방어구만 넣어주면 그 캐릭터의 체력과 공격력은 기본값을 가지게 된다.
마지막으로 체력, 공격력, 방어구 모두 입력값으로 넣을 수도 있게 만들었다.
그런 다음 방어구 클래스에 방어구를 뚫고 들어온 데미지를 구해주는 함수를 만들었다. 그런데 방어력이 아주 높은 캐릭터를 직접 만들어보니 방어력이 공격력보다 높은 경우 '데미지-방어력'이 음수가 될 수도 있다는 것을 알게되었다. 그렇기 때문에 공격을 당했음에도 체력이 오히려 오르는 경우가 발생했다.
문제를 해결하기 위해 1-1.에서 했던 것과는 조금 다르게 방어력이 데미지보다 높은 경우에는 데미지를 받지 않도록 코드를 바꿔주었다.
[작성한 코드]
fun main() {
val character1 = Character()
val character2 = Character(200, 30)
val character3 = Character(Armor())
val character4 = Character(300, 50, Armor(40))
// '캐릭터1(공격력10)'이 '캐릭터2(체력200,공격력30)'를 공격한 뒤 '캐릭터2'의 체력
character1.attack(character2)
println(character2.health) // 190
// '캐릭터2(공격력30)'가 '캐릭터3(체력100,방어구5)'을 공격한 뒤 '캐릭터3'의 체력
character2.attack(character3)
println(character3.health) // 75
// '캐릭터4(공격력50)'이 '캐릭터1(체력100)'을 공격한 뒤 '캐릭터1'의 체력
character4.attack(character1)
println(character1.health) // 50
// '캐릭터2(공격력30)'가 '캐릭터4(체력300,방어력40)'를 공격한 뒤 '캐릭터4'의 체력
character2.attack(character4)
println(character4.health) // 300
}
class Character(var health: Int, val power: Int, val armor: Armor?) {
constructor() : this(100, 10, null)
constructor(_health: Int, _power: Int) : this(_health, _power, null)
constructor(_armor: Armor) : this(100, 10, _armor)
fun attack(target:Character) {
target.attacked(power)
}
fun attacked(damage:Int) {
health -= armor?.damageDealtThroughArmor(damage) ?: damage
}
}
class Armor(val shield: Int) {
constructor() : this(5)
fun damageDealtThroughArmor(damage: Int): Int {
if (damage - shield > 0) {
return damage - shield
} else return 0
}
}
2. 오늘 배운 것
- 객체지향에 더 깊게 공부해보았다.
- 클린코드와 리팩토링에 대해 공부해보았다.