'Add second character axis to ggplot2
I'm looking to present the results of a user survey of semantic differential scores as a boxplot, with the first semantic displayed along the default x-axis, and the second semantic displayed along a secondary top x-axis. In simple terms this can be represented as
df <- data.frame(sem1 = c("good", "big", "red"), sem2 = c("bad", "small", "blue"), a = c(4,6,1), b = c(6,2,6), c = c(8,3,7))`
df %>% pivot_longer(!c(sem1, sem2), names_to = "user", values_to = "score") %>%
ggplot() +
geom_boxplot(aes(x = sem1, y = score)) +
ylim(1,10)
Which produces the below chart. What I would really like to do is add the characters from sem2 (i.e. "bad", "small", "blue") to a secondary axis along the top of the graph. Everything I've seen using sec_axis seems geared towards transformations of numerical axes rather than the addition of a distinct second axis. I believe I could possibly use annotation_custom to add each semantic one by one, but in my actual application with ~15 semantic pairs to display this could get quite cumbersome. Is there a more elegant way?
Solution 1:[1]
You seem to only check if any door is in range and then open/close whatever is currently assigned to _door. And that simply seems to not be what you expect it to be.
You should not only check
if(_playerBubble.doorIsInRange)
but actually rather store the door that is in range
public Door doorInRange;
void OnTriggerEnter(Collider other)
{
// There is no need for a Tag at all!
// Rather simply check if the object contains a Door (or derived) component
if (other.TryGetComponent<Door>(out var door))
{
doorInRange = door;
}
}
void OnTriggerExit(Collider other)
{
if (other.gameObject.TryGetCompoment<Door>(out var _))
{
doorInRange = null;
}
}
and then only in the PlayerController script rather do
if (Input.GetButtonDown("Open"))
{
// In this case this is rather the implicit bool conversion
// and basically equals a check for != null
if(_playerBubble.doorInRange)
{
_playerBubble.doorInRange.ManageDoor();
}
}
The doors themselves don't need to double check whether they are in range.
And then personally I'd not use that int to differ between door types. Rather use inheritance and have your base class Door
public class Door : MonoBehaviour
{
private bool _iAmOpen = false;
public void ManageDoor ()
{
// Invert the flag
_iAmOpen = !_iAmOpen;
if(_iAmOpen)
{
// simply log the name of this door object, no need for an additional field
// by also including a context you can simply click once
// on the logged message in the console and this object will be highlighted
Debug.Log($"{name} is opened", this);
Open();
}
else
{
Debug.Log($"{name} is closed", this);
Close();
}
}
protected virtual void Open()
{
// Whatever a default door does
}
protected virtual void Close()
{
// Whatever a default door does
}
}
and then rather extend and modify the behavior slightly
public class Drawer : Door
{
public float drawerSpeed = 1f;
protected override void Open()
{
// Do different things for a drawer
// if you want to also include the default stuff additionally include a call to
//base.Open();
}
protected override void Close()
{
// Do different things for a drawer
// if you want to also do the default stuff additionally include a call to
//base.Close();
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |

