Я надеюсь, что эти разговоры прояснят мой вопрос - я бы полностью понял, если они этого не сделают, поэтому дайте мне знать, если это так, и я постараюсь прояснить себя.
Познакомьтесь с BoxPong , очень простой игрой, которую я сделал, чтобы познакомиться с разработкой объектно-ориентированных игр. Перетащите коробку, чтобы контролировать мяч и собирать желтые вещи.
Создание BoxPong помогло мне сформулировать, среди прочего, фундаментальный вопрос: как я могу иметь объекты, которые взаимодействуют друг с другом без необходимости «принадлежать» друг другу? Другими словами, есть ли способ для объектов не быть иерархическими, а вместо этого сосуществовать? (Я буду вдаваться в подробности ниже.)
Я подозреваю, что проблема сосуществования объектов является распространенной, поэтому я надеюсь, что есть надежный способ ее решения. Я не хочу изобретать квадратное колесо, поэтому я думаю, что идеальный ответ, который я ищу, это «вот шаблон дизайна, который обычно используется для решения ваших проблем».
Особенно в простых играх, таких как BoxPong, ясно, что существует или должно быть несколько объектов, сосуществующих на одном уровне. Там есть коробка, есть шар, есть предмет коллекционирования. Все, что я могу выразить на объектно-ориентированных языках, хотя - или так кажется - это строгие отношения HAS-A . Это делается с помощью переменных-членов. Я не могу просто начать ball
и позволить этому делать свое дело, мне нужно, чтобы он постоянно принадлежал другому объекту. Я настроил его так , чтобы главный объект игры имеет коробку, а коробка в свою очередь , имеет мяч, и имеет надрубленной счетчик. Каждый объект также имеетupdate()
метод, который вычисляет положение, направление и т. д., и я иду аналогичным образом: я вызываю метод обновления основного игрового объекта, который вызывает методы обновления всех его потомков, а они в свою очередь вызывают методы обновления всех своих потомков. Это единственный способ создать объектно-ориентированную игру, но я чувствую, что это не идеальный способ. В конце концов, я бы не думал, что мяч принадлежит к ящику, а скорее находится на одном уровне и взаимодействует с ним. Я полагаю, что этого можно добиться, превратив все игровые объекты в переменные-члены основного игрового объекта, но я не вижу, что это решает что-либо. Я имею в виду ... оставляя в стороне очевидный беспорядок, как мог бы быть шар и коробка узнать друг друга , то есть взаимодействовать?
Есть также проблема объектов, нуждающихся в передаче информации между собой. У меня довольно большой опыт написания кода для SNES, где у вас есть доступ практически ко всей оперативной памяти. Скажем, вы делаете своего собственного врага для Super Mario World , и вы хотите, чтобы он убрал все монеты Марио, а затем просто сохранил ноль для адреса $ 0DBF, без проблем. Нет никаких ограничений на то, что враги не могут получить доступ к статусу игрока. Я думаю, что я был избалован этой свободой, потому что с C ++ и тому подобным я часто задаюсь вопросом, как сделать значение доступным для некоторых других объектов (или даже глобальных).
На примере BoxPong, что, если бы я хотел, чтобы мяч отскакивал от краев экрана? width
и height
являются свойствами Game
класса,ball
иметь доступ к ним. Я мог бы передавать такие значения (либо через конструкторы, либо через методы, где они необходимы), но это просто кричит о плохой практике.
Я предполагаю, что моя главная проблема в том, что мне нужны объекты, чтобы знать друг друга, но единственный способ, которым я могу это сделать, - это строгая иерархия, которая уродлива и непрактична.
Я слышал о «классах друзей» на C ++ и вроде знаю, как они работают, но если они представляют собой конечное решение, то почему я не вижу friend
ключевых слов, разбросанных по каждому отдельному проекту C ++, и почему концепция не существует в каждом языке ООП? (То же самое касается указателей на функции, о которых я только недавно узнал.)
Заранее спасибо за ответы любого рода - и еще раз, если есть часть, которая не имеет смысла для вас, дайте мне знать.
Ответы:
В общем, очень плохо получается, если объекты одного уровня знают друг о друге. Как только объекты узнают друг о друге, они связаны или связаны друг с другом. Это делает их трудно изменить, трудно проверить, трудно поддерживать.
Это работает намного лучше, если есть некоторый объект «выше», который знает о них и может устанавливать взаимодействия между ними. Объект, который знает о двух одноранговых узлах, может связать их вместе посредством внедрения зависимости, или посредством событий, или посредством передачи сообщений (или любого другого механизма разъединения). Да, это приводит к некоторой искусственной иерархии, но это намного лучше, чем беспорядок спагетти, который вы получаете, когда вещи просто взаимодействуют вольно-невольно. Это только более важно в C ++, поскольку вам нужно что-то, чтобы владеть временем жизни объектов.
Короче говоря, вы можете сделать это, просто привязав объекты повсюду друг к другу с помощью специального доступа, но это плохая идея. Иерархия обеспечивает порядок и четкое владение. Главное помнить, что объекты в коде не обязательно являются объектами в реальной жизни (или даже в игре). Если объекты в игре не создают хорошую иерархию, лучше использовать другую абстракцию.
источник
Нет!
Я думаю, что главная проблема, с которой вы столкнулись, это то, что вы воспринимаете «объектно-ориентированное программирование» слишком буквально. В ООП объект представляет собой не «вещь», а «идею», которая означает, что «Мяч», «Игра», «Физика», «Математика», «Дата» и т. Д. Все являются допустимыми объектами. Также не требуется, чтобы объекты «знали» о чем-либо. Например,
Date.Now().getTommorrow()
спросите компьютер, какой сегодня день, примените тайные правила даты, чтобы выяснить дату завтрашнего дня, и верните ее вызывающему.Date
Объект не знает ни о чем еще, это нужно только запрашивает информацию по мере необходимости из системы. Кроме того,Math.SquareRoot(number)
не нужно ничего знать, кроме логики того, как рассчитать квадратный корень.Таким образом, в вашем примере, который я привел, «Мяч» не должен ничего знать о «Ящике». Коробки и мячики - совершенно разные идеи, и не имеют права разговаривать друг с другом. Но физический движок знает, что такое Box и Ball (или, по крайней мере, ThreeDShape), и он знает, где они находятся и что с ними должно происходить. Поэтому, если шар сжимается из-за холода, Физический Двигатель скажет этому экземпляру шара, что теперь он меньше.
Это как строить машину. Компьютерная микросхема ничего не знает о двигателе автомобиля, но автомобиль может использовать компьютерную микросхему для управления двигателем. Простая идея использовать маленькие, простые вещи вместе, чтобы создать немного большую, более сложную вещь, которая сама по себе может использоваться в качестве компонента других более сложных частей.
А в вашем примере с Марио, что если вы находитесь в комнате испытаний, где прикосновение к врагу не истощает монеты Мариоса, а просто выбрасывает его из этой комнаты? За пределами области идей Марио или врага Марио должен потерять монеты, касаясь врага (фактически, если у Марио есть звезда неуязвимости, он убивает врага вместо этого). Таким образом, любой объект (домен / идея), который отвечает за то, что происходит, когда Марио касается врага, является единственным, который должен знать обо всем, и должен делать что угодно с любым из них (в той степени, в которой этот объект допускает внешние управляемые изменения). ).
Кроме того, с вашими утверждениями об объектах, вызывающих дочерние элементы
Update()
, это чрезвычайно подвержено ошибкам, как, если бы ониUpdate
вызывались несколько раз за кадр от разных родителей? (даже если вы поймете это, это потраченное впустую процессорное время, которое может замедлить вашу игру) Каждый должен касаться только того, что ему нужно, когда это необходимо. Если вы используете Update (), вы должны использовать некоторую форму шаблона подписки, чтобы убедиться, что все обновления вызываются один раз за кадр (если это не обрабатывается для вас, как в Unity)Изучение того, как определить ваши доменные идеи в четкие, изолированные, четко определенные и простые в использовании блоки, будет самым большим фактором, насколько хорошо вы сможете использовать ООП.
источник
Похоже, вы упускаете суть объектно-ориентированного программирования.
Объектно-ориентированное программирование - это управление зависимостями путем выборочного инвертирования определенных ключевых зависимостей в вашей архитектуре, чтобы вы могли предотвратить жесткость, хрупкость и невозможность повторного использования.
Что такое зависимость? Зависимость - это зависимость от чего-то другого. Когда вы храните ноль для адреса $ 0DBF, вы полагаетесь на тот факт, что этот адрес находится там, где расположены монеты Марио, и что монеты представлены в виде целого числа. Ваш пользовательский код врага зависит от кода, реализующего Марио и его монеты. Если вы сделаете изменение в том, где Марио хранит свои монеты в памяти, вы должны вручную обновить весь код, ссылающийся на ячейку памяти.
Объектно-ориентированный код - это все, чтобы ваш код зависел от абстракций, а не от деталей. Так что вместо
ты бы написал
Теперь, если вы хотите изменить то, как Марио хранит свои монеты с int на long или double, или хранить их в сети, или хранить в базе данных, или запустить какой-то другой длинный процесс, вы вносите изменения в одном месте: Класс Mario, и весь ваш другой код продолжает работать без изменений.
Поэтому, когда вы спрашиваете
Вы действительно спрашиваете:
который не является объектно-ориентированным программированием.
Я предлагаю вам начать с прочтения всего здесь: http://objectmentor.com/omSolutions/oops_what.html, а затем найти в youtube все Роберта Мартина и посмотреть все это.
Мои ответы исходят от него, а некоторые прямо цитируются им.
источник
mario.coins = 0;
, ноmario.loseCoins();
, что хорошо и верно - но я хочу сказать, как враг может иметь доступ кmario
объекту в любом случае?mario
быть переменной-членомenemy
мне не кажется правильным.Вы можете включить слабую связь, применив шаблон посредника. Реализация этого требует, чтобы у вас была ссылка на компонент-посредник, который знает все принимающие компоненты.
Он понимает, что такое «мастер игры, пожалуйста, пусть это случится».
Обобщенным шаблоном является шаблон публикации-подписки. Это уместно, если посредник не должен содержать много логики. В противном случае используйте созданный вручную посредник, который знает, куда направить все вызовы и, возможно, даже изменить их.
Существуют синхронные и асинхронные варианты, обычно называемые шиной событий, шиной сообщений или очередью сообщений. Найдите их, чтобы определить, подходят ли они в вашем конкретном случае.
источник