• Content count

  • Joined

  • Last visited

Community Reputation

199 Neutral

About SolarChronus

  • Rank
  1. C++ Interface Review

    alnite, thank you for your help.   That last post allowed me to take a step back and look at my interfaces in a different light and enum states in general. 
  2. Timing game loop

    I may have asked this the wrong way.  What I meant was what kind of time step should I use so that it doesn't look like the sprites are flying across the screen at mach 3?     Try doing what I suggested above, and when you're manipulating the speed of your sprites, make sure to multiply the speed by the game time.   I.E   objectPosition.x = objectPosition.x + (speed * gameTime).   This would make sure the speed value for your object is only as fast as the time step. The longer it takes for the next frame (or loop) to begin, the greater that speed value will end up being, and vice versa.   Say it takes .161 seconds to go from frame 1 to frame 2, and you have a speed of 50.  50 * .161. Doing the math would give you a 'speed for this frame' of 8.05, and thus would only move the sprite by that much, instead of the full 50.
  3. Timing game loop

    If you're not using a fixed time step or waiting for vsync, you may want to keep track of the time between frames. Which in your case it's pretty simple, a frame is each run of your game loop.   You should start a clock right before your game loop starts. At the beginning of each loop save the clock time and restart the clock. This would typically be called the game time as it'll be the time between the previous from to the beginning of the next frame. You would then pass this game time around to the various parts of your game to make sure everything is executed at the correct speed. I.E  movement of objects, physics, etc.
  4. C++ Interface Review

    That's a pretty good question, looking at my code I only use the state information to choose which OnMouseOver method should be fired, but beyond that, I output it to a debug log.    The mouse events are being maintained via a struct, every frame I figure out what the state of all the buttons and its position is, then save it for use by the various classes that want to know about the mouse state.   Here is the handle input function if it'll make it easier to see how I'm using the mouse over state variable. void TextButton::HandleInput(Mouse mouse) { // Don't accept input if the button is disabled. if(isEnabled == false) return; bool isMouseOver = IsMouseOver(mouse.mousePosition); //Set MouseOverState if(isMouseOver) { if(mouseOverState == MouseOverState::None) { OnMouseOverBegin(); } else if(mouseOverState == MouseOverState::Begin) { OnMouseOverStay(); } if(Input::IsLeftMouseButtonPressed()) { OnMouseLeftClickPressed(); } if(Input::IsLeftMouseButtonHeld()) { text.SetColor(pressedColor); } if(Input::IsLeftMouseButtonReleased()) { OnMouseLeftClickReleased(); } } else { if(mouseOverState == MouseOverState::Stay) { OnMouseOverEnd(); } else if(mouseOverState == MouseOverState::End) { mouseOverState = MouseOverState::None; } } }
  5. C++ Interface Review

    Ok, I think I see. So instead of calling the "SetMouseOverState(...)" and passing in the desired state. I would just instead call the correct behavior induced method with the correct state?     So I would change my current OnMouseOverBegin(...) implementation from: void TextButton::OnMouseOverBegin(void) { text.SetColor(hoverColor); } To this? void TextButton::OnMouseOverBegin(const MouseOverState::MouseOverState& state) { mouseOverState = state; text.SetColor(hoverColor); } I can see that lightening up the if chain I'm using so I wouldn't have both the Set(...) and then call the appropriate OnMouseOver___ method, but since I've removed the setter like that would I even need to pass in the desired state? Would this be appropriate? void TextButton::OnMouseOverBegin(void) { mouseOverState = MouseOverState::Begin; text.SetColor(hoverColor); }
  6. C++ Interface Review

    Can you explain why you have the state inside the methods? Are you assuming that each method would be called, sending a referenced state variable, and that method would only act if the state is the correct one?
  7. Hey everyone,   Creating some classes here and wanted to make sure I was doing my interface stuff in a maintainable way.    I created a TextButton that reacts to mouse overs, clicks, position/scale/rotation can be manipulated, and has a label. I created  an interface for each of those 'elements'.   ILabel IButton IMouseOver IMouseLeftClick GameObject   My TextButton inherits them  works perfectly as designed. As I was moving onto creating other elements, such as an ImageButton which will react the same as the TextButton but display an image instead, I wanted to get some feedback to make sure my current interfaces are pretty scale-able..   One note of concern that sticks out to me. In my IMouseOver interface I added a protected member for keeping track of the mousesState. Now I know, typically, interfaces are supposed to be explicit contracts which should only contain methods and not members. I felt having to define that particular member in every single class that inherits it would be super redundant - it's only accessed by the Setter/Getter in the same interface. (Which I also feel is a bit odd to implement those methods in every derived class since their behavior would never alter).   So if anyone could give this a once over and offer up their advice on the way I'm headed, I'd really appreciate it!  Thanks!   ILabel.hpp #pragma once #include <string> class ILabel { public: virtual ~ILabel(void) {}; virtual void SetString(std::string string) =0; virtual std::string GetString(void) const =0; }; IButton.hpp #pragma once class IButton { public: virtual ~IButton(void) {}; virtual void Enable(void) =0; virtual void Disable(void) =0; virtual bool IsEnabled(void) const =0; }; IMouseOver.hpp #pragma once namespace MouseOverState { enum MouseOverState { None =0, Begin, Stay, End }; }; class IMouseOver { public: virtual ~IMouseOver(void) {}; virtual bool IsMouseOver(sf::Vector2i mousePosition) =0; virtual void OnMouseOverBegin(void) =0; virtual void OnMouseOverStay(void) = 0; virtual void OnMouseOverEnd(void) =0; virtual void SetMouseOverState(MouseOverState::MouseOverState mos) { mouseOverState = mos;}; virtual MouseOverState::MouseOverState GetMouseOverState(void) const { return mouseOverState; }; protected: MouseOverState::MouseOverState mouseOverState; }; IMouseLeftClick.hpp #pragma once class IMouseLeftClick { public: virtual ~IMouseLeftClick(void) {}; virtual void OnMouseLeftClickPressed(void) =0; virtual void OnMouseLeftClickReleased(void) =0; virtual void AddLeftClickEvent(boost::function<void()> signal) =0; }; TextButton.hpp #pragma once #include <string> #include "boost/signals2.hpp" #include "GameObject.hpp" #include "ILabel.hpp" #include "IButton.hpp" #include "IMouseOver.hpp" #include "IMouseLeftClick.hpp" #include "Text.hpp" #include "Input.hpp" class TextButton: public GameObject, public virtual ILabel, public virtual IButton, virtual IMouseOver, virtual IMouseLeftClick { public: TextButton(void); TextButton(std::string fontName, int fontSize, sf::Color idleColor, sf::Color hoverColor, sf::Color pressedColor, sf::Color disabledColor, std::string string); ~TextButton(void); //TextButton void HandleInput(Key key, Mouse mouse); //GameObject void Update(float gameTime); void Draw(sf::RenderWindow* window, float gameTime); void SetPosition(sf::Vector2f); //ILabel void SetString(std::string string); std::string GetString(void) const; //IButton void Enable(void); void Disable(void); bool IsEnabled(void) const; sf::FloatRect GetWorldBounds(void) const; //IMouseOver bool IsMouseOver(sf::Vector2i mousePosition); void OnMouseOverBegin(void); void OnMouseOverStay(void); void OnMouseOverEnd(void); //IMouseLeftClick void OnMouseLeftClickPressed(void); void OnMouseLeftClickReleased(void); void AddLeftClickEvent(boost::function<void()> signal); private: Text text; sf::FloatRect bounds; bool isEnabled; sf::Color idleColor; sf::Color hoverColor; sf::Color pressedColor; sf::Color disabledColor; boost::signals2::signal<void ()> leftClickSignals; };
  8. So I'm onto rolling my own classes for UI use for fun and learning. I've create a bunch of interfaces to manage the UI widgets but I'm at a fork in the road on the more complex widgets I want to create.   Right now my UI code consists of the following: // Interfaces IGUIWidget {} IReactsToClick {} IReactsToHover {} IProgressBar : IUIWidget {} IImage : IUIWidget {} IButton : IUIWidget, IReactsToClick, IReactsToHover {} ILabel : IUIWidget {} // Concrete Label : ILabel {} Image : IImage {} ProgressBar : IProgressBar {} Button : IButton {} ImageButton : IImage, IButton {} LabelButton : ILabel, IButton {} Now for the 'complex' widget.   I have an object that has the following properties: - Acts like a button - Has a background image - Has a content specific image - Has multiple smaller images that change (can be any number). - Has a label - Has a progress bar    Here is the interface and concrete class I've come up with:  public interface IComplexWidget : IGUIWidget { IButton button { get; } IImage background { get; } IImage contextImage { get; } IList<IImage> multiImages { get; } ILabel label { get; } IProgressBar progressBar { get; } } public class ComplexWidget : IComplexWidget { public ComplexWidget() { } public Button button { get; protected set; } public Image background { get; protected set; } public Image contextImage { get; protected set; } public IList<Image> multiImages { get; protected set; } public Label label { get; protected set; } public ProgressBar progressBar { get; protected set; } } Would this be the correct path for creating more complex widgets? 
  9. Unity Code Review - Event Use?

    Thank you Telastyn! Everything seems to work a lot better (not to mention a lot of less lines of code). I never thought about 'tying various other single responsibilities together' as a single responsibility itself. You've been a great help.
  10. Unity Code Review - Event Use?

    Thank you, that helped a lot. I adapted your approach to my current requirements and came up with this. public interface Subsystem { void IncreasePowerLevel(); bool IsPowered { get; } bool IsPowerMaxed { get; } int MaximumPowerCapacity { get; } int CurrentPowerCapacity { get; } int PowerLevel { get; } PowerRelay PrimaryPowerRelay { get; } PowerRelay SecondaryPowerRelay { get; } } public interface PowerRelay { bool IncreaseThroughput(Subsystem target); bool IsAtCapacity { get; } int MaximumPowerThroughput { get; } int CurrentThroughput { get; } IEnumerable<KeyValuePair<Subsystem, int>> PowerAllocation { get; } } public interface PowerSource { void AllocatePowerToSubsystem(Subsystem target); int maximumPowerSupplyCapacity { get; } int currentPowerSupplyCapacity { get; } int currentPowerSupply { get; } IEnumerable<KeyValuePair<Subsystem, int>> PowerAllocation { get; } } Whenever the user increases a subsystems power it can only happen as long as it isn't maxed out, the primary/secondary relay feeding it isn't at capacity and the power source has an available block.   My only hangup now is when I call the AllocatePowerToSubsystem(..). Since the power source, relay, and subsystem need to have available power requirements before anything is assigned I need to do some simple checks.   I contemplated doing something like below. I know it would work fine, but in the interest of the single responsibility principle, would it be ok?  public AllocatePowerToSubsystem(Subsystem target) { if(target.IsPowerMaxed == false) { if(target.PrimaryPowerRelay.IsAtCapacity == false) { target.IncreasePower(); target.PrimaryPowerRelay.IncreaseThroughput(target); PowerAllocation[target]+= 1; } else if(target.SecondaryPowerRelay.IsAtCapacity == false) { target.IncreasePower(); target.SecondaryPowerRelay.IncreaseThroughput(target); PowerAllocation[target]+= 1; } } }
  11. Unity Code Review - Event Use?

    Thanks for the reply, but it wasn't a very useful comment. Besides using events to notify my classes that things are happening, what would you suggest? I was taught that events should be used when you want to notify other classes of something happening and pass some data using a custom event arg if needed. The only other way I see writing this is if I pass instances of the relay, warp core, and subsystem to each other. Feels over kill when I just need to do a request notification and receive an answer and not needing access to all the members of the rest of the classes.
  12. Unity Code Review - Event Use?

    Posting here always seems to get my brain juices flowing a bit better and I've re-written my code to use events more extensively.    Explanation of my event call chain. When the user makes a power request (by clicking on an increase subsystem power icon), the button notifies the appropriate subsystem via an event that it needs to start a power request. The subsystem then first checks to make sure its even capable of getting more power and if possible, notifies its parent power relay via calling a power request event. The appropriate relay receives this event and checks to make sure that the relay can handle more power flowing through it. If it cannot, it does a callback event to the subsystem denying the power request, if it can, it calls another power request event that the WarpCore is listening to. The warp core checks if it has any available power and either allocates power and does a successful power request callback, or if it cannot, it calls a refused power request callback. Finally if warpcore sends back a successful request then the relay will increase its power load and the subsystem will allocate more power to itself. If the call wasn't a success, the relay will notify the subsystem of the failure and neither will increase their power usage. ....Wow that sounds way more complicated than it was to code.   I've posted the classes below to get any potential feedback on my method! I love code reviews. The classes are in event call order as best as possible. public class ControlButton : MonoBehaviour { private EventButton eventButton = null; private Subsystem subsystem = null; public void Initialize(Subsystem subsystem) { eventButton = this.gameObject.ValidateComponent<EventButton>(); eventButton.onLeftClick += subsystem.AllocatePowerRequest; eventButton.onRightClick += subsystem.DeallocatePowerRequest; this.subsystem = subsystem; } } public class Subsystem : MonoBehaviour { public event EventHandler OnAllocationChange; public event EventHandler OnPotentialAllocationChange; public event EventHandler OnWantPower; public Console console = null; public Equipment equipment = null; public Relay relay = null; public virtual void InitializeNew(int currentHitPoints, int maximumHitPoints, int currentLevel, int maximumLevel, object[] abilities) { equipment.CurrentHitPoints = currentHitPoints; equipment.MaximumHitPoints = maximumHitPoints; IsActive = true; CurrentLevel = currentLevel; MaximumLevel = maximumLevel; Abilities = abilities; HookEvents(); } private void HookEvents() { equipment.Damaged += EquipmentDamaged; console.Damaged += ConsoleDamaged; relay.OnApprovedRequestPower += AllocatePower; relay.OnRefuseRequestPower += NotGivenPower; } private void AllocatePower(object sender, EventArgs e) { PowerTransferInfoEventArgs powerTransferInfo = (PowerTransferInfoEventArgs)e; if (powerTransferInfo.Subsystem == this) DoAllocation(); } private void NotGivenPower(object sender, EventArgs e) { // TODO: Do something showing we were not given power. } public virtual void AllocatePowerRequest(object sender, EventArgs e) { if (ActiveLevel < CurrentLevel) { if (OnWantPower != null) { PowerTransferInfoEventArgs powerTransferInfo = new PowerTransferInfoEventArgs(); powerTransferInfo.Subsystem = this; OnWantPower(this, powerTransferInfo); } } } public virtual void DeallocatePowerRequest(object sender, EventArgs e) { DoDeallocation(); } public virtual void DoAllocation() { Debug.Log("Increase Active Level..."); ActiveLevel = Mathf.Clamp(ActiveLevel + 1, 0, CurrentLevel); AllocationChangeNotify(); } public virtual void DoDeallocation() { ActiveLevel = Mathf.Clamp(ActiveLevel - 1, 0, CurrentLevel); AllocationChangeNotify(); } public virtual void IncreaseAllocationPotential() { CurrentLevel = Mathf.Clamp(CurrentLevel + 1, 0, MaximumLevel); AllocationChangeNotify(); } public virtual void DecreaseAllocationPotential() { CurrentLevel = Mathf.Clamp(CurrentLevel - 1, 0, MaximumLevel); AllocationChangeNotify(); } private void AllocationChangeNotify() { Debug.Log("Active Level Notidy..."); if (OnAllocationChange == null) return; SubsystemPowerLevelEventArgs info = new SubsystemPowerLevelEventArgs(CurrentLevel, ActiveLevel, MaximumLevel); OnAllocationChange(this, info); } } public class Relay : MonoBehaviour { public event EventHandler OnRequestPower; public event EventHandler OnRefuseRequestPower; public event EventHandler OnApprovedRequestPower; private int currentLevel = 0; private int maxLevel = 0; public Relay() { } public void ListenForPowerRequestFrom(Subsystem subsystem) { subsystem.OnWantPower += RequestPowerFromWarpCore; } public void RequestPowerFromWarpCore(object sender, EventArgs e) { // Cancel the power request if the relay is at capacity. if (currentLevel == maxLevel) { RefusePowerRequest(this, e); return; } if (OnRequestPower != null) { PowerTransferInfoEventArgs powerTransferInfo = (PowerTransferInfoEventArgs)e; powerTransferInfo.Relay = this; OnRequestPower(this, powerTransferInfo); } } public void ApprovePowerRequest(object sender, EventArgs e) { PowerTransferInfoEventArgs powerTransferInfo = (PowerTransferInfoEventArgs)e; if (powerTransferInfo.Relay == this) { if (OnApprovedRequestPower != null) OnApprovedRequestPower(this, powerTransferInfo); IncreaseLevel(); } } public void RefusePowerRequest(object sender, EventArgs e) { PowerTransferInfoEventArgs powerTransferInfo = (PowerTransferInfoEventArgs)e; if (powerTransferInfo.Relay == this) { if (OnRefuseRequestPower != null) OnRefuseRequestPower(this, powerTransferInfo); } } private bool IncreaseLevel() { if (currentLevel < maxLevel) { currentLevel++; return true; } return false; } private bool DecreaseLevel() { if (currentLevel > 0) { currentLevel--; return true; } return false; } } public class WarpCore { public EventHandler OnSuccesfulAllocation; public EventHandler OnSuccessfulDeallocation; public EventHandler OnFailedAllocation; public EventHandler OnFailedDeallocation; private int absoluteMaximumAllocation = 31; private int maximumAllocators = 0; private Dictionary<PowerAllocator, Subsystem> allocators = new Dictionary<PowerAllocator, Subsystem>(); public WarpCore(int maximumPowerCells, int currentUpgradeLevel, int maximumUpgradeLevel) { for (int i = 0; i < absoluteMaximumAllocation; i++) { PowerAllocator powerAllocator = new PowerAllocator(i, AllocatorStatus.Hidden); allocators.Add(powerAllocator, null); } } public void ListenForRelayPowerRequests(Relay relay) { relay.OnRequestPower += PowerAllocationRequest; //Setup callbacks to the relay OnSuccesfulAllocation += relay.ApprovePowerRequest; OnFailedAllocation += relay.RefusePowerRequest; } public void PowerAllocationRequest(object sender, EventArgs e) { PowerTransferInfoEventArgs powerTransferInfo = (PowerTransferInfoEventArgs)e; if (GetAvailablePowerCell() != null) { AllocateTo(powerTransferInfo.Subsystem); if (OnSuccesfulAllocation != null) OnSuccesfulAllocation(this, powerTransferInfo); } else { if (OnFailedAllocation != null) OnFailedAllocation(this, powerTransferInfo); } } public void IncreaseWarpCoreAllocationPotential() { foreach (KeyValuePair<PowerAllocator, Subsystem> allocationSet in allocators) { if (allocationSet.Key.AllocatorStatus == AllocatorStatus.Hidden) allocationSet.Key.SetStatus(AllocatorStatus.Unused); } maximumAllocators++; } public void AllocateTo(Subsystem subsystem) { PowerAllocator allocation = GetAvailablePowerCell(); allocators[allocation] = subsystem; allocation.SetStatus(AllocatorStatus.Used); } // TODO: Add deallocation methods }
  13. So I wanted to get some opinions on how I'm going about using events to control two classes from a button UI click.  I don't feel its entirely "right" as in the sense it's breaking some SOLID rules. I don't believe the button itself should be checking if an allocation can happen, and adding instances of the Subsystem and WarpCore classes to it seem wrong too, just unsure of a better way.   using UnityEngine; using System; using System.Collections; public class ControlButton : MonoBehaviour {     private EventButton eventButton = null;     private Subsystem subsystem = null;     private WarpCore warpCore = null;     public void Initialize(Subsystem subsystem, WarpCore warpCore)     {         eventButton = this.gameObject.ValidateComponent<EventButton>();         eventButton.onLeftClick += Allocate;         eventButton.onRightClick += Deallocate;         this.subsystem = subsystem;         this.warpCore = warpCore;     }     private void Allocate(object sender, EventArgs e)     {         if (warpCore.CanAllocate() == true && subsystem.CanAllocate() == true)         {             warpCore.Allocate(subsystem);             subsystem.Allocate();         }       }     private void Deallocate(object sender, EventArgs e)     {         // TODO: Remove the allocation from the subsystem and free up an allocation on the warp core.     } }  
  14. Connecting two independent classes

    Those last 4 posts were so insightful I've solved my problem. I had to go back and refactor most of my UI (which allowed me to delete a lot of gunk code) and it works beautifully now.   Thank you for the suggestions and advice! I'm a better programmer for it.
  15. Connecting two independent classes

    @AllEightUp   I love that solution and know of quite a few places I can already apply it and some future areas as well. So I'm trying to apply it to solve this current dilemma.   However, I still have the problem of supplying the players inventory. The player isn't really a game object or movable object in the game. The game itself is a series of locations and when the user changes locations all that's done is a change of the area and activation of any in-game events that haven't been triggered.   The player class is really just a data container of their attributes, their inventory, their status, etc. With a few methods for modifying that data, TakeDamage() for example.   So when a new location is loaded a list of events (not to be confused with "Events") are invoked. I derive differing events from a common interface which allows me to stack differing events together. I.E A message box event and then force a combat event.   So when the user goes to a location which has a ShopEvent, that ShopEvent is invoked and populates a new shop with items. Then I do what you suggested above with that form of callback, passing in my UI (my UI class is a static class), passing in the shop inventory, and... I don't have the player inventory. So I was able to solve not having an instance of the shop inventory, but I'm lost as to how to incorporate the player.   Looking back through my code, I initialize the player, hook up a few Events so the UI can change as the players values are modified(health, etc), and that's it. Unsure of how to get an instance of the player inventory into the ShopEvent.   I could modify the base interface that all my game events are derived from, but other events would need to implement that version of Invoke, even if they're never going to use it.