MVC shop project for software architecture, in Unity.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

198 lines
9.5 KiB

using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
/// <summary>
/// This class connects a grid view for buy state of the shop to a controller to manipulate the BuyModel via a ShopController
/// interface, it contains specific methods to setup and update a grid view, with the data from a BuyModel. If you want to display
/// informationoutside of the BuyModel, for example, the money amount from the player's inventory, then you need to either keep a
/// reference to all the related models, or make this class an observer/event subscriber of the related models.
/// </summary>
public abstract class ShopView : MonoBehaviour
{
public ShopModel ShopModel => model; //A getter to access shopModel. Will access inventory of trade partner in sell mode!
[SerializeField]
protected GameObject itemPrefab; //A prefab to display an item in the view
[SerializeField]
protected Button buyButton;
[SerializeField]
protected TextMeshProUGUI instructionText;
[SerializeField] protected LayoutGroup layoutGroup; // The layout group that represents this view visually
[SerializeField] private ModelComponent ownModel; // Reference to the model this view technically belongs to
[SerializeField] private InventoryComponent tradePartner; // Reference to the model this view technically belongs to
protected ShopModel model; // Model in MVC pattern
protected ShopModel other; // Other model in MVC pattern (our own inventory)
protected ShopController shopController; //Controller in MVC pattern
private ItemType itemFilter = ItemType.All; // View can filter items, and this is the filter we want to use for that
// Set up the view's necessary stuff before the view is enabled
protected virtual void Awake()
{
//model = new BuyModel(2f, 16, 500); //Right now use magic values to set up the shop
Debug.Assert(ownModel != null,"No shop model assigned!",this);
Debug.Assert(tradePartner != null,"No trade partner assigned!",this);
model = ownModel.Model;
shopController = gameObject.AddComponent<MouseController>().Initialize(model);//Set the default controller to be the mouse controller
SetupItemIconView(); //Setup the grid view's properties
InitializeButtons(); //Connect the buttons to the controller
//model.Subscribe(this);
}
private void OnEnable()
{
// sanity check
if (model == null) model = ownModel.Model;
RegisterMoneyObserver(); // Make sure if this thing's got a money component somewhere we register it as an observer for the inventory we're looking at
model.SetTradePartner(tradePartner.Inventory);
PopulateItemIconView(); //Display items
}
private void OnDisable()
{
ClearIconView();
model.SetTradePartner(null);
UnregisterMoneyObserver();
}
// If view is generic, and shopview was just an implementation, this would be more flexible in theory. But for now it's enough to just manually assign "ourselves" as trade partner if we know the model works on just one inventory
private void RegisterMoneyObserver()
{
var moneyComp = GetComponentInChildren<InventoryMoneyDisplay>();
if (moneyComp != null) tradePartner.Inventory.RegisterObserver(moneyComp);
}
// Undo the above
private void UnregisterMoneyObserver()
{
var moneyComp = GetComponentInChildren<InventoryMoneyDisplay>();
if (moneyComp != null) tradePartner.Inventory.RemoveObserver(moneyComp);
}
//------------------------------------------------------------------------------------------------------------------------
// SetupItemIconView()
//------------------------------------------------------------------------------------------------------------------------
//Setup the grid view according to the ViewConfig object's requirements, right now it just sets the constraint mode and column count,
//you can make cosmetic adjustments to the GridLayoutGroup by adding more configurations to ViewConfig and use them adjusting properties
//like cellSize, spacing, padding, etc.
protected abstract void SetupItemIconView();
//------------------------------------------------------------------------------------------------------------------------
// RepopulateItems()
//------------------------------------------------------------------------------------------------------------------------
//Clears the grid view and repopulates it with new icons (updates the visible icons)
private void RepopulateItemIconView() {
ClearIconView();
PopulateItemIconView();
}
//------------------------------------------------------------------------------------------------------------------------
// PopulateItems()
//------------------------------------------------------------------------------------------------------------------------
//Adds one icon for each item in the shop
private void PopulateItemIconView() {
foreach (Item item in model.inventory.GetItems()) {
if(itemFilter == ItemType.All || item.GetItemType() == itemFilter)
AddItemToView(item);
}
}
//------------------------------------------------------------------------------------------------------------------------
// ClearIconView()
//------------------------------------------------------------------------------------------------------------------------
//Removes all existing icons in the gridview
protected abstract void ClearIconView();
//------------------------------------------------------------------------------------------------------------------------
// AddItemToView()
//------------------------------------------------------------------------------------------------------------------------
//Adds a new item container to the view, each view can have its way of displaying items
protected abstract void AddItemToView(Item item);
protected abstract void RemoveItemFromView(Item item);
//------------------------------------------------------------------------------------------------------------------------
// InitializeButtons()
//------------------------------------------------------------------------------------------------------------------------
//This method adds a listener to the 'Buy' button. They are forwarded to the controller. Since this is the confirm button of
//the buy view, it will just call the controller interface's ConfirmSelectedItem function, the controller will handle the rest.
private void InitializeButtons() {
buyButton.onClick.AddListener(
delegate {
shopController.ConfirmSelectedItem();
}
);
}
// Set the item filter and automatically repopulate. Could use bit flags probably...
public void SetItemFilter(ItemType filter)
{
this.itemFilter = filter;
RepopulateItemIconView();
}
private void Update()
{
//RepopulateItemIconView();//Repopulate the view each frame, this is very inefficient and won't work in many scenarios and SHOULD NOT be in
//the final implementation, the view should be modified by the models via an observer or event queue pattern
//Switch between mouse and keyboard controllers
if (Input.GetKeyUp(KeyCode.K))
{
if (shopController is MouseController)
{
SwitchToKeyboardControl();
}
}
else if (Input.GetMouseButtonUp(0))
{
if (shopController is GridViewKeyboardController)
{
SwitchToMouseControl();
}
}
//Let the current controller handle input
shopController.HandleInput();
}
//------------------------------------------------------------------------------------------------------------------------
// SwitchToKeyboardControl()
//------------------------------------------------------------------------------------------------------------------------
protected abstract void SwitchToKeyboardControl();
//------------------------------------------------------------------------------------------------------------------------
// SwitchToMouseControl()
//------------------------------------------------------------------------------------------------------------------------
protected abstract void SwitchToMouseControl();
// These three are from the observable interface, and it was decided to leave this stuff to the implementation for now. Keeping here for reference
// public virtual void OnSelected(Item item)
// {
// Debug.Log("View selects item: " + item.name,this);
// RepopulateItemIconView();
// }
//
// public virtual void OnRemoved(Item item)
// {
// RemoveItemFromView(item);
// }
//
// public virtual void OnAdded(Item item)
// {
// throw new NotImplementedException();
// }
}