Я использую Haskell для создания игры (это задание, поэтому не судите меня), но у меня возникает проблема с типами данных.Абстрактные записи или интерфейсы записи?
Так что я хочу, это тип данных Entity, который имеет местоположение, скорость, угол и скорость вращения. Запись очень хорошо подходит для этой идеи:
data Entity = Entity {
location :: Vector,
velocity :: Vector,
angle :: Float,
rotation :: Float
}
Теперь мне нужны экземпляры Entity, а именно Player Rock Pickup и Bullet. Но у игроков Rocks и Bullets должно быть дополнительное поле, а именно health :: Int, а Pickup должен иметь другое дополнительное поле, а именно pickupType :: PickupType.
Но у меня есть определенные методы, которые я хочу использовать для любого типа Entity. Например:
move :: Entity -> Entity
move [email protected](Entity {location, velocity, angle, rotation}) = e {location = location + velocity, angle = angle + rotation}
Я понятия не имею, как это сделать или если это возможно. Я бы не понял, почему, если это невозможно, так как это возможно на других языках.
Некоторые попытки, и почему они не совсем то, что я хочу:
Попытка 1:
type Player = Player {
e :: Entity,
health :: Int
}
Это работает, но это действительно некрасиво. Это, например, то, как вы перемещаете Игрок:
movePlayer :: Player -> Player
movePlayer [email protected](e) = p {e = move e}
Это просто действительно уродливо.
Положительные: Простота создания абстрактного класса. Простота создания экземпляров. Простые абстрактные методы.
Отрицательные: Трудно получить или задать объекты, реализуемые сущностью.
Попытка 2:
class Entity e where
getLocation :: e -> Vector
getVelocity :: e -> Vector
...
setLocation :: Vector -> e -> e
setVelocity :: Vector -> e -> e
...
data Player = Player {
playerLocation :: Vector,
playerVelocity :: Vector,
...
playerHealth :: Int
}
instance Entity Player where
getLocation = location
getVelocity = velocity
...
setLocation l e = e {location = l}
setVelocity v e = e {playerVelocity = v}
...
move :: (Entity e) => e -> e
move e = (setLocation (getLocation e + getVelocity e) . setAngle (getAngle e + getRotation e)) e
Ну это работает, но я надеюсь, что мы все можем согласиться, что их определения теперь действительно некрасиво. Абстрактные методы, которые работают на любой Entity, также становятся уродливыми. Единственное, что нравится movePlayer, - это очень просто.
movePlayer :: Player -> Player
movePlayer = move
Мне больше не нужно определять movePlayer, так как я могу просто использовать move.
Положительные стороны: Легко получить или задавать области экземпляра экземпляра объекта.
Отрицательные: Трудно создать абстрактный класс. Еще труднее создавать экземпляры. Твердые абстрактные методы.
Покушение 3:
Дайте Entity все поля, необходимые любой экземпляр.
data Entity = Entity {
location :: Vector,
velocity :: Vector,
angle :: Float,
rotation :: Float,
health :: Int,
pickupType :: PickupType
}
Таким образом, мне даже не нужно определять экземпляры, и я могу просто использовать Entity. Единственная проблема заключается в том, что у вас много избыточных данных.В настоящее время я использую и IMO - лучшее решение для моей проблемы, но мне все еще не нравится.
Положительные: Легко создать абстрактный класс, даже если он не является абстрактным. Не нужно указывать экземпляры. Простые абстрактные методы. Простота получения или установки полей, реализуемых сущностью экземпляра.
Отрицательные: Много неиспользованных данных. Каждый раз, когда вы создаете Entity, вам нужно определить множество бессмысленных полей.
Так что, пожалуйста, помогите мне, я не могу найти более эффективные методы, чем эти три :(
Последний метод по своей сути плохой, потому что слишком много полей всегда подразумевают несогласованное состояние. – ThreeFx
Кроме того, нет ничего плохого в использовании Haskell для написания игр :) – duplode