I'm pulling my hair out trying the figure this out. I have a simple button, that checks for the mouse to be over, and then changes the texture if it is. It works fine. However, when I add a camera into the mix, it breaks everything. I've tried transforming both the mouse and rectangle I use for bounding-box collision, and it won't work. Here's my code for the button:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace x.Graphics.UI
{
public enum ButtonStates
{
Normal,
Hover,
Pressed
}
public delegate void ButtonPress();
public class Button
{
public Texture2D Texture
{
get
{
Texture2D result = null;
switch (ButtonState)
{
case ButtonStates.Normal:
result = NormalTexture;
break;
case ButtonStates.Hover:
result = HoverTexture;
break;
case ButtonStates.Pressed:
result = DownTexture;
break;
}
return result;
}
}
public Vector2 Position { get; set; }
public event ButtonPress ButtonPressed;
public ButtonStates ButtonState { get; set; }
public Rectangle CollisionRect { get; set; }
private Texture2D NormalTexture;
private Texture2D HoverTexture;
private Texture2D DownTexture;
private MouseState mouseState;
private MouseState previousMouseState;
public Button(Texture2D normalTexture, Texture2D hoverTexture, Texture2D downTexture,
Vector2 position)
{
NormalTexture = normalTexture;
HoverTexture = hoverTexture;
DownTexture = downTexture;
Position = position;
mouseState = Mouse.GetState();
previousMouseState = mouseState;
CollisionRect = new Rectangle((int)Position.X, (int)Position.Y,
Texture.Width,
Texture.Height);
}
public void Update (MouseState currentState)
{
mouseState = currentState;
if (CollisionRect.Contains(new Point(mouseState.X, mouseState.Y)))
{
if (mouseState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed)
{
ButtonState = ButtonStates.Pressed;
ButtonPressed();
}
else
ButtonState = ButtonStates.Hover;
}
else
ButtonState = ButtonStates.Normal;
}
public void Update(MouseState currentState, Camera camera)
{
Vector2 mouse = new Vector2(mouseState.X, mouseState.Y);
mouse = Vector2.Transform(mouse, camera.InverseTransform);
CollisionRect = CalculateTransformedBoundingBox(CollisionRect, c.InverseTransform);
Console.WriteLine("Rectangle[X: {0}, y: {1}], Mouse:[X: {2}, Y: {3}]", CollisionRect.X, CollisionRect.Y, mouse.X, mouse.Y);
if (CollisionRect.Contains(new Point((int)mouse.X, (int)mouse.Y)))
{
if (mouseState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed)
{
ButtonState = ButtonStates.Pressed;
ButtonPressed();
}
else
ButtonState = ButtonStates.Hover;
}
else
ButtonState = ButtonStates.Normal;
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(Texture, Position, null, Color.White, 0.0f,
Vector2.Zero, 1.0f, SpriteEffects.None, 1.0f);
}
private Rectangle CalculateTransformedBoundingBox(Rectangle local, Matrix toWorldSpace)
{
Vector2 leftTop = new Vector2(local.Left, local.Top);
Vector2 rightTop = new Vector2(local.Right, local.Top);
Vector2 leftBottom = new Vector2(local.Left, local.Bottom);
Vector2 rightBottom = new Vector2(local.Right, local.Bottom);
Vector2.Transform(ref leftTop, ref toWorldSpace,
out leftTop);
Vector2.Transform(ref rightTop, ref toWorldSpace,
out rightTop);
Vector2.Transform(ref leftBottom, ref toWorldSpace,
out leftBottom);
Vector2.Transform(ref rightBottom, ref toWorldSpace,
out rightBottom);
// Find the minimum and maximum extents of the
// rectangle in world space
Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop),
Vector2.Min(leftBottom, rightBottom));
Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop),
Vector2.Max(leftBottom, rightBottom));
// Return that as a rectangle
return new Rectangle((int)min.X, (int)min.Y,
(int)(max.X - min.X), (int)(max.Y - min.Y));
}
}
}
And my code for the camera:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace x.Graphics
{
public class Camera
{
protected float _zoom;
protected Matrix _transform;
protected Matrix _inverseTransform;
protected Vector2 _pos;
protected float _rotation;
protected Viewport _viewport;
protected MouseState _mState;
protected KeyboardState _keyState;
protected Int32 _scroll;
public float Zoom
{
get { return _zoom; }
set { _zoom = value; }
}
public Matrix Transform
{
get { return _transform; }
set { _transform = value; }
}
public Matrix InverseTransform
{
get { return _inverseTransform; }
}
public Vector2 Pos
{
get { return _pos; }
set { _pos = value; }
}
public float Rotation
{
get { return _rotation; }
set { _rotation = value; }
}
public Camera(Viewport viewport)
{
_zoom = 1.0f;
_scroll = 1;
_rotation = 0.0f;
_pos = Vector2.Zero;
_viewport = viewport;
}
public void Update()
{
Input();
MathHelper.Clamp(_zoom, 0.01f, 10.0f);
_rotation = ClampAngle(_rotation);
_transform = Matrix.CreateRotationZ(_rotation) *
Matrix.CreateScale(new Vector3(_zoom, _zoom, 1)) *
Matrix.CreateTranslation(_pos.X, _pos.Y, 0);
_inverseTransform = Matrix.Invert(_transform);
}
protected virtual void Input()
{
_mState = Mouse.GetState();
_keyState = Keyboard.GetState();
//Check Move
if (_keyState.IsKeyDown(Keys.A))
{
_pos.X += 10f;
}
if (_keyState.IsKeyDown(Keys.D))
{
_pos.X -= 10f;
}
if (_keyState.IsKeyDown(Keys.W))
{
_pos.Y -= 10f;
}
if (_keyState.IsKeyDown(Keys.S))
{
_pos.Y += 10f;
}
}
protected float ClampAngle(float radians)
{
while (radians < -MathHelper.Pi)
{
radians += MathHelper.TwoPi;
}
while (radians > MathHelper.Pi)
{
radians -= MathHelper.TwoPi;
}
return radians;
}
}
}
I'm not 100% sure what's wrong, but the mouse position only changes when I press a button. I'm really confused, I've never worked with cameras before. Any help would be really appreciated. Thanks!
UPDATE:
It detects the mouse as being over the button before I try and move the camera. After that, the coorindates of the rectangle are continually incremented.
Don't transform bounding box... is easy to transform mouse coordinates ... ;)
Use the inverse transform of your camera matrix to transform mouse coords to the same space of your bounding box.
Related
WebCamTexture works fine on editor, but doesn't work on Build. I have tried: turn off antivirus and firewall, check authorization, put the webcamtexture on UI_RawImage and a plane gameObject, nothing works on build. There is no any real conclusion about the same bug on the forums (many are about Vuforia, and I just want to take a photo by a simple webcam on Windows). Any clues? Below the code. Tks in advance.
using UnityEngine;
using System.Collections;
using System.IO;
using UnityEngine.UI;
using System.Collections.Generic;
// https://stackoverflow.com/questions/24496438/can-i-take-a-photo-in-unity-using-the-devices-camera
public class GetCam2 : MonoBehaviour
{
WebCamTexture webCam;
//string your_path = "D:\\Lixo\\FotosCam";// any path you want to save your image
[SerializeField] string your_path = "";
public RawImage display;
public AspectRatioFitter fit;
public int contadorFotos;
[SerializeField] GameObject planoWeb;
IEnumerator Start()
{
your_path = "" + Application.dataPath;
yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);
if (Application.HasUserAuthorization(UserAuthorization.WebCam)) {
LigaCamComeco1();
}
}
void LigaCamComeco1() {
if (WebCamTexture.devices.Length == 0)
{
Debug.LogError("can not found any camera!");
return;
}
int index = -1;
for (int i = 0; i < WebCamTexture.devices.Length; i++)
{
if (WebCamTexture.devices[i].name.ToLower().Contains("pc"))
{
Debug.Log("WebCam Name:" + WebCamTexture.devices[i].name + " Webcam Index:" + i);
index = i;
}
}
if (index == -1)
{
Debug.LogError("can not found your camera name!");
return;
}
WebCamDevice device = WebCamTexture.devices[index];
webCam = new WebCamTexture(device.name);
webCam.Play();
display.texture = webCam;
planoWeb.GetComponent<Renderer>().materials[0].mainTexture = webCam;
}
public void Update()
{
if (Input.GetKeyDown(KeyCode.PageUp)) {
Ratio();
callTakePhoto();
}
}
void Ratio() {
float ratio = (float)webCam.width / (float)webCam.height;
fit.aspectRatio = ratio;
float ScaleY = webCam.videoVerticallyMirrored ? -1f : 1f;
display.rectTransform.localScale = new Vector3(1f, ScaleY, 1f);
int orient = -webCam.videoRotationAngle;
display.rectTransform.localEulerAngles = new Vector3(0, 0, orient);
}
public void callTakePhoto() // call this function in button click event
{
StartCoroutine(TakePhoto());
}
IEnumerator TakePhoto() // Start this Coroutine on some button click
{
yield return new WaitForEndOfFrame();
Texture2D photo = new Texture2D(webCam.width, webCam.height);
photo.SetPixels(webCam.GetPixels());
photo.Apply();
byte[] bytes = photo.EncodeToJPG();
File.WriteAllBytes(your_path + "\\ZaxisCam" + contadorFotos + ".jpg", bytes);
contadorFotos++;
}
}
I have tried: turn off antivirus and firewall, check authorization, put the webcamtexture on UI_RawImage and a plane gameObject, nothing works on build.
When the player hits the ground the camera shakes and when he jumps he just get's teleported up in an unsmooth manner
public class PlayerController : MonoBehaviour {
CharacterController controller;
Vector3 motion = Vector3.zero;
#region Movement Variables
public float walkingSpeed = 4f;
public float runningSpeed = 6f;
public float jumpSpeed = 5f;
public float gravity = 9.8f;
#endregion Movement Variables
void Start () {
controller = GetComponent<CharacterController>();
}
void Update () {
motion = Vector3.zero;
motion.x = Input.GetAxis("Horizontal");
if (controller.isGrounded && Input.GetAxis("Vertical")>0) motion.y += jumpSpeed;
if (Input.GetKey(KeyCode.LeftShift))
{
motion.x *= runningSpeed;
}
else
{
motion.x *= walkingSpeed;
}
motion.y -= gravity;
}
void FixedUpdate()
{
controller.Move(motion * Time.deltaTime);
}
}
I'm trying to create smooth movement and currently it is not reliable at all. thanks for any helpers
Take controller.Move(motion * Time.deltaTime); out of FixedUpdate and put it at the very end of Update method.
In case you have some script on camera as well, for example, some implementation of a follow camera where you're working with the transform component of that camera in the Update loop, that should go in LateUpdate instead.
See this https://learn.unity.com/tutorial/update-and-fixedupdate#5c8a4242edbc2a001f47cd63
and https://docs.unity3d.com/ScriptReference/MonoBehaviour.LateUpdate.html
This implementation of a player controller for GameObject that has a Character Controller component may also help you.
void Update()
{
if (isDead) return;
isWalking = Input.GetButton("Walk");
isGrounded = characterController.isGrounded;
if (isGrounded)
{
// Move
float verticalAxis = Input.GetAxis("Vertical");
moveDirection = new Vector3(0, 0, verticalAxis);
moveDirection = transform.TransformDirection(moveDirection);
if (verticalAxis > 0 && isWalking)
{
moveDirection *= walkingSpeed;
}
else if (verticalAxis > 0)
{
moveDirection *= runningSpeed;
}
// Jump
if (Input.GetButtonDown("Jump"))
{
moveDirection.y = jumpSpeed;
//moveDirection.z = jumpDistance; in case some forward boost is required
moveDirection = transform.TransformDirection(moveDirection);
}
}
// Gravity
moveDirection.y -= gravity * Time.deltaTime;
characterController.Move(moveDirection * Time.deltaTime);
// Rotation
float horizontalAxis = Input.GetAxis("Horizontal");
transform.Rotate(0, horizontalAxis, 0);
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.FromToRotation(transform.up, GetHitNormal()) * transform.rotation, yRotationSpeed * Time.deltaTime);
}
// To keep local up aligned with global up on a slope ground
private Vector3 GetHitNormal()
{
RaycastHit hit;
if (Physics.Raycast(transform.position, Vector3.down, out hit))
return hit.normal;
else
return Vector3.zero;
}
NOTE: I'm using Xamarin.Mac, but I believe the intent and/or missteps should be clear enough to most swift Cocoa developers.
PROBLEM:
My custom filter is not being applied to the view it backs.
EXAMPLE
var builtInFilter = new CIColorInvert();
builtInFilter.SetDefaults();
var customFilter = new HazeFilter();
customFilter.SetDefaults();
//In both cases here, the Image and OutputImage properties will have a null value
Layer.Filters = new CIFilter[1]{builtInFilter}; //Works
Layer.Filters = new CIFilter[1]{customFilter}; //Does nothing
The problem isn't that my custom filter isn't capable of doing anything. When I assign its Image property and draw its OutputImage it works as expected.
This tells me that the Kernel and OutputImage method are functioning properly.
Source Code
HazeFilter.cs
public class HazeFilter : CIFilter
{
static CIKernel hazeRemovalKernel;
public HazeFilter () : base()
{
if (hazeRemovalKernel == null) {
hazeRemovalKernel = CIKernel.FromProgramSingle(#"
kernel vec4 myHazeRemovalKernel(sampler src, __color color, float distance, float slope)
{
vec4 t;
float d;
d = destCoord().y * slope + distance;
t = unpremultiply(sample(src, samplerCoord(src)));
t = (t-d*color)/(1.0-d);
return premultiply(t);
}");
}
}
public override void SetDefaults ()
{
base.SetDefaults ();
inputColor = CIColor.FromCGColor (NSColor.Purple.CGColor);
inputDistance = 0.8;
inputSlope = 0.002;
}
CIImage image;
[Export("inputImage")]
public new CIImage Image
{
get { return image; }
set {
WillChangeValue ("inputImage");
image = value;
DidChangeValue ("inputImage");
}
}
CIColor color;
[Export("inputColor")]
public CIColor inputColor
{
get { return color; }
set {
WillChangeValue ("inputColor");
color = value;
DidChangeValue ("inputColor");
}
}
NSNumber distance;
[Export("inputDistance")]
public NSNumber inputDistance{
get { return distance; }
set {
WillChangeValue ("inputDistance");
distance = value;
DidChangeValue ("inputDistance");
}
}
NSNumber slope;
[Export("inputSlope")]
public NSNumber inputSlope{
get { return slope; }
set {
WillChangeValue ("inputSlope");
slope = value;
DidChangeValue ("inputSlope");
}
}
[Export("outputImage")]
public new CIImage OutputImage
{
get
{
if (Image == null)
return null;
var inputSampler = new CISampler (Image);
var argumentArray = NSArray.FromNSObjects (new NSObject[] {
inputSampler,
Runtime.GetNSObject (inputColor.Handle),
inputDistance,
inputSlope
});
return Apply (hazeRemovalKernel, argumentArray, null);
}
}
}
Canvas.cs
public class Canvas : NSView
{
public Canvas (CGRect rect) : base(rect)
{
WantsLayer = true;
Layer.BackgroundColor = NSColor.Clear.CGColor;
Layer.MasksToBounds = true;
Layer.NeedsDisplayOnBoundsChange = true;
LayerUsesCoreImageFilters = true;
}
CIFilter effect;
public CIFilter Effect {
get { return effect; }
set{
effect = value;
Layer.Filters = new CIFilter[1]{Effect};
NeedsDisplay = true;
}
}
}
Instantiation
var builtInFilter = new CIColorInvert();
builtInFilter.SetDefaults();
var customFilter = new HazeFilter();
customFilter.SetDefaults();
wrapPanel.Effect = builtInFilter; //Works as expected
wrapPanel.Effect = customFilter; //Does nothing
I have a custom class as follows which works fine, the button grows/shrinks to accomodate the text and the bg image changes on a click.
Probem I want to solve is how to "fadeIN" one or other image when clicked/notClicked is called
Here is my code
public ExpandingOvalButton(String text) {
if (text.length() > 15) {
label.getElement().getStyle().setFontSize(20, Unit.PX);
} else {
label.getElement().getStyle().setFontSize(30, Unit.PX);
}
int width = 120;
initWidget(panel);
label.setText(text);
// width = width + (text.length() * 8);
String widthStr = width + "px";
image.setWidth(widthStr);
image.setHeight("100px");
button = new PushButton(image);
button.setWidth(widthStr);
button.setHeight("50px");
panel.add(button, 0, 0);
panel.add(label, 18, 14);
}
public void isClicked()
{
image.setUrl("images/rectangle_green.png");
}
public void unClicked()
{
image.setUrl("images/rectangle_blue.png");
}
#Override
public HandlerRegistration addClickHandler(ClickHandler handler) {
return addDomHandler(handler, ClickEvent.getType());
}
public void setButtonEnabled(boolean enabled) {
// panel.setVisible(enabled);
// this.label.setVisible(enabled);
this.button.setVisible(enabled);
}
Here's a general utility class to fade any element:
public class ElementFader {
private int stepCount;
public ElementFader() {
this.stepCount = 0;
}
private void incrementStep() {
stepCount++;
}
private int getStepCount() {
return stepCount;
}
public void fade(final Element element, final float startOpacity, final float endOpacity, int totalTimeMillis) {
final int numberOfSteps = 30;
int stepLengthMillis = totalTimeMillis / numberOfSteps;
stepCount = 0;
final float deltaOpacity = (float) (endOpacity - startOpacity) / numberOfSteps;
Timer timer = new Timer() {
#Override
public void run() {
float opacity = startOpacity + (getStepCount() * deltaOpacity);
DOM.setStyleAttribute(element, "opacity", Float.toString(opacity));
incrementStep();
if (getStepCount() == numberOfSteps) {
DOM.setStyleAttribute(element, "opacity", Float.toString(endOpacity));
this.cancel();
}
}
};
timer.scheduleRepeating(stepLengthMillis);
}
}
Calling code for instance:
new ElementFader().fade(image.getElement(), 0, 1, 1000); // one-second fade-in
new ElementFader().fade(image.getElement(), 1, 0, 1000); // one-second fade-out
You could use GwtQuery. It provides fadeIn & fadeOut effects (and many other JQuery goodies), it is cross-browser compatible and seems to be pretty active.
I am working on a WP7 game. I am using Game state managment (http://create.msdn.com/en-US/education/catalog/sample/game_state_management , but I think its not important ) I have problem with saving data into
Microsoft.Phone.Shell.PhoneApplicationService.Current.State
if I put there sprite in this method
public override void Deactivate()
{
#if WINDOWS_PHONE
Microsoft.Phone.Shell.PhoneApplicationService.Current.State["Score"] = Score;
Microsoft.Phone.Shell.PhoneApplicationService.Current.State["cloudSprite"] = cloudSprite;
#endif
base.Deactivate();
}
there is nothing in
Microsoft.Phone.Shell.PhoneApplicationService.Current.State
in activate method. However if I remove cloudSprite and put there only Score which is int it works fine. I dont know whats wrong maybe it cant handle more complex objects. I tried also float doubel, it all works. But if I put there something more complex it doesnt work. What do you think ?
EDIT
This is my sprite class. I dont know how to make it serializable. I have added there [DataContractAttribute()] and [DataMember] but its not working
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using System.Runtime.Serialization;
using System.IO;
namespace GameStateManagementSample.GameObjects
{
[DataContractAttribute()]
public class Sprite
{
[DataMember]
public Vector2 Position;
[DataMember]
public Vector2 Size;
[DataMember]
public Texture2D Texture;
[DataMember]
public Rectangle Rect
{
get
{
return new Rectangle((int)Position.X, (int)Position.Y, (int)Size.X, (int)Size.Y);
}
}
public Sprite(Vector2 position)
{
Position = position;
}
public Sprite(Vector2 position, Vector2 size)
{
Position = position;
Size = size;
}
public Sprite(Vector2 position, Texture2D texture)
{
Position = position;
Texture = texture;
Size = new Vector2(Texture.Width, Texture.Height);
}
public void LoadContent(string assetName, ContentManager content)
{
Texture = content.Load<Texture2D>(assetName);
if (Size == Vector2.Zero)
Size = new Vector2(Texture.Width, Texture.Height);
}
public virtual void Draw(SpriteBatch spriteBatch)
{
//spriteBatch.Draw(Texture, Rect, Color.White);
spriteBatch.Draw(Texture, Position, Color.White);
}
public virtual void Draw(SpriteBatch spriteBatch, Rectangle TexturePositionInSpriteSheet, Color color)
{
spriteBatch.Draw(Texture, Position, TexturePositionInSpriteSheet, color);
}
public bool Intersects(Vector2 point)
{
if (point.X >= Position.X && point.Y >= Position.Y && point.X <= Position.X + Size.X && point.Y <= Position.Y + Size.Y)
return true;
else return false;
}
public bool Intersects(Rectangle rect)
{
return Rect.Intersects(rect);
}
public static void Serialize(Stream streamObject, object objForSerialization)
{
if (objForSerialization == null || streamObject == null)
return;
DataContractSerializer ser = new DataContractSerializer(objForSerialization.GetType());
ser.WriteObject(streamObject, objForSerialization);
}
public static object Deserialize(Stream streamObject, Type serializedObjectType)
{
if (serializedObjectType == null || streamObject == null)
return null;
DataContractSerializer ser = new DataContractSerializer(serializedObjectType);
return ser.ReadObject(streamObject);
}
}
}
Objects added to the State collection are serialized with the DataContractSerializer. Ensure that anything you're saving there can be serialized that way.
Any serialization errors are silently ignored.
Update
Here's a simplified version of your Sprite object:
[DataContract]
public class Sprite
{
[DataMember]
public Vector2 Position;
[DataMember]
public Vector2 Size;
[DataMember]
public Texture2D Texture;
public Sprite()
{
}
public Sprite(Vector2 position)
{
Position = position;
}
public Sprite(Vector2 position, Vector2 size)
{
Position = position;
Size = size;
}
public Sprite(Vector2 position, Texture2D texture)
{
Position = position;
Texture = texture;
Size = new Vector2(Texture.Width, Texture.Height);
}
}
And here's an example of it being serialized and deserialized:
// Sprite serialization test
var sprite1 = new Sprite(new Vector2(12.34f, 56.78f));
Sprite sprite2;
using (var memStr = new MemoryStream())
{
var serializer = new DataContractSerializer(typeof(Sprite));
serializer.WriteObject(memStr, sprite1);
memStr.Position = 0;
var sr = new StreamReader(memStr);
var serialized = sr.ReadToEnd();
// serialized now looks like
// <Sprite xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/MiscExperiments"><Position xmlns:d2p1="http://schemas.datacontract.org/2004/07/Microsoft.Xna.Framework"><d2p1:X>12.34</d2p1:X><d2p1:Y>56.78</d2p1:Y></Position><Size xmlns:d2p1="http://schemas.datacontract.org/2004/07/Microsoft.Xna.Framework"><d2p1:X>0</d2p1:X><d2p1:Y>0</d2p1:Y></Size><Texture xmlns:d2p1="http://schemas.datacontract.org/2004/07/Microsoft.Xna.Framework.Graphics" i:nil="true" /></Sprite>
memStr.Position = 0;
sprite2 = (Sprite)serializer.ReadObject(memStr);
// sprite2 now contains the same as
// sprite2.Position = { X:12.34, Y:56.78 }
}
I believe that you get the ArgumentOutOfRangeException which is silently ignored if you aren't debugging (in which case the exception is thrown). The items stored in the State dictionary need to be serializable. Try storing that sprite somehow and saving only a link (source) to the sprite in the state dictionary.