Xamarin Compass Rotate Image North - xamarin

I got this simple CompassView in my Xamarin.iOS Applikation.
Everything works as expected, but I cant find a way to rotate the image so its always facing north.
Here is my CompassOnReadingChanged Method:
private void CompassOnReadingChanged(object sender, CompassChangedEventArgs e)
{
var data = e.Reading.HeadingMagneticNorth;
this.BeginInvokeOnMainThread(() =>
{
this.Transform = CGAffineTransform.MakeRotation((nfloat)data * (nfloat)Math.PI / 180);
});
}
this.Transform works because the class inherits UIView.
Thank you for your help
-Simon

It was showing the direction the device was facing and not where north is.
So this does the trick:
private void CompassOnReadingChanged(object sender, CompassChangedEventArgs e)
{
var data = e.Reading.HeadingMagneticNorth;
this.BeginInvokeOnMainThread(() =>
{
this.Transform = CGAffineTransform.MakeRotation(-(nfloat)data * (nfloat)Math.PI / 180);
});
}

Related

How can I build slanted dashed line in Xamarin application

I want to display a slanted dashed line in my application that would look something like
this. using Line API in xamarin I was able to create dashed line, but I haven't had any luck with making each dash slanted. I also looked at skiasharp library for Xamarin but didn't find anything which can help with slanting.
#jason thank you for your response, I was so much focused on getting "Slanted dashed line" what you suggested never crossed my mind. Although now I have the solution for my issue.
public partial class SlantedDashedView : ContentView
{
private readonly SKPaint paint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = Color.Red.ToSKColor()
};
private readonly SKRect rect = new SKRect
{
Location = new SKPoint(0, 0),
Size = new SKSize(25, 30)
};
public SlantedDashedView()
{
InitializeComponent();
mainCanvas.InvalidateSurface();
}
private void MainCanvas_PaintSurface(object sender, SKPaintSurfaceEventArgs e)
{
SKImageInfo info = e.Info;
SKSurface surface = e.Surface;
SKCanvas canvas = surface.Canvas;
int currentY = 0;
canvas.Skew(0, -1);
while (currentY < info.Height)
{
canvas.DrawRect(rect, paint);
currentY += 45;
canvas.Translate(0, 45);
}
}
}
Here is the result

LibGDX / Box2d - only one rectangle is being rendered by debugrenderer

I'm revisiting LibGDX game programming and I am unfortunately having to re-learn stuff I used to know.
I'm currently using Tiled Map Editor to make a very simple Donkey Kong style level. I have around 20 rectangles in total for the level.
I've created a box2d world in my main GameScreen class and have a for loop to get the rectangle objects into the world and debugrenderer.
My problem is that only the bottom (and first) rectangle I drew is showing up. I have checked the scale, also I put a println() which tells me the object information has been parsed with all the rectangles info showing correct (ie. the rectangles x,y,w,h values) but as I say, only one rectangle shows up on the debugrenderer.
I've just got back into programming after around 6month break and so I'm hoping i've missed something simple. The same code in my old projects still works fine as I've tested some.
Here is my code, any help is massively appreciated. Thanks
public class GameScreen implements Screen {
SpriteBatch spriteBatch;
OrthographicCamera cam;
Viewport v;
TmxMapLoader mapLoader;
TiledMap map;
OrthogonalTiledMapRenderer mapRenderer;
World world;
Box2DDebugRenderer b2dr;
float mapScale = 10f/140f;
public GameScreen(){
spriteBatch = new SpriteBatch();
cam = new OrthographicCamera();
v = new FitViewport(Constants.V_WIDTH, Constants.V_HEIGHT, cam);
cam.setToOrtho(false, v.getWorldWidth(), v.getWorldHeight());
mapLoader = new TmxMapLoader();
map = mapLoader.load("level1.tmx");
mapRenderer = new OrthogonalTiledMapRenderer(map, mapScale);
world = new World(new Vector2(0,-9.8f), true);
b2dr = new Box2DDebugRenderer();
// box2d local variables
BodyDef bdef = new BodyDef();
PolygonShape shape = new PolygonShape();
FixtureDef fdef = new FixtureDef();
Body body;
// create platform object rectangles
for (MapObject object : map.getLayers().get(2).getObjects().getByType(RectangleMapObject.class)){
Rectangle rect = ((RectangleMapObject)object).getRectangle();
bdef.type = BodyDef.BodyType.StaticBody;
bdef.position.set(rect.getX() + rect.getWidth() / 2 * mapScale, rect.y + rect.getHeight() / 2 * mapScale);
body = world.createBody(bdef);
shape.setAsBox(rect.getWidth() / 2 * mapScale, rect.getHeight() / 2 * mapScale);
fdef.shape = shape;
body.createFixture(fdef);
}
}
#Override
public void show() {
}
#Override
public void render(float delta) {
update(delta);
clearScreen();
draw();
}
public void update(float dt){
mapRenderer.setView(cam);
}
public void clearScreen(){
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
}
public void draw(){
spriteBatch.setProjectionMatrix(cam.combined);
mapRenderer.render();
b2dr.render(world, cam.combined);
spriteBatch.begin();
spriteBatch.end();
}
#Override
public void resize(int width, int height) {
v.update(width, height);
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
spriteBatch.dispose();
}
}
Sorry for wasting time here. I have fixed this.
It was just that I hadnt applied the scale of the map also to the x,y of the rectangles. So i change one line like so, and now it works:
bdef.position.set(rect.getX() * mapScale + rect.getWidth() / 2 * mapScale, rect.y * mapScale + rect.getHeight() / 2 * mapScale);

Win10 App - Holding & Releasing the map to manipulate an element on the interface

I working on an UWP (Win10) App with a simple location picker function. The user can drag the map on the wanted location. A basic Pushpin thats always in the center of the Map window acts as the location indicator. It works just like the free location pick in WhatsApp.
To give the user feedback that he is moving the center pin, I want to raise the pin when the user is moving the map and lower it again on release.
Here the simple code to raise the pin (and manipulate the shadow):
private void MyMap_MapHolding(MapControl sender, MapInputEventArgs args)
{
iconSwitch = true;
if(iconSwitch == true) {
centerPin.Margin = new Thickness(0, 0, 0, 60);
centerPinShadow.Opacity = 0.3;
centerPinShadow.Width = 25;
}
But this event doesn't seem to be affected on click & hold or tap & hold. Am I missing something?
FYI: I tried this out with the MyMap_MapTapped(...) method, and it worked just fine, but I need it when the map is dragged not just tapped.
Chees!
I've tested and debugged, MapHolding event can't work by me either. For your purpose, CenterChangedLink event maybe helpful, I've tested it too.
Here is part of my sample code:
RandomAccessStreamReference mapIconStreamReference;
public Maptest()
{
this.InitializeComponent();
myMap.Loaded += MyMap_Loaded;
myMap.MapTapped += MyMap_MapTapped;
myMap.MapHolding += MyMap_MapHolding;
myMap.CenterChanged += MyMap_CenterChanged;
mapIconStreamReference = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/MapPin.png"));
}
private void MyMap_Loaded(object sender, RoutedEventArgs e)
{
myMap.Center =
new Geopoint(new BasicGeoposition()
{
//Geopoint for Seattle
Latitude = 47.604,
Longitude = -122.329
});
myMap.ZoomLevel = 12;
}
private void MyMap_MapTapped(Windows.UI.Xaml.Controls.Maps.MapControl sender, Windows.UI.Xaml.Controls.Maps.MapInputEventArgs args)
{
var tappedGeoPosition = args.Location.Position;
string status = "MapTapped at \nLatitude:" + tappedGeoPosition.Latitude + "\nLongitude: " + tappedGeoPosition.Longitude;
rootPage.NotifyUser( status, NotifyType.StatusMessage);
}
private void MyMap_MapHolding(Windows.UI.Xaml.Controls.Maps.MapControl sender, Windows.UI.Xaml.Controls.Maps.MapInputEventArgs args)
{
var holdingGeoPosition = args.Location.Position;
string status = "MapHolding at \nLatitude:" + holdingGeoPosition.Latitude + "\nLongitude: " + holdingGeoPosition.Longitude;
rootPage.NotifyUser(status, NotifyType.StatusMessage);
}
private void MyMap_CenterChanged(Windows.UI.Xaml.Controls.Maps.MapControl sender, object obj)
{
MapIcon mapIcon = new MapIcon();
mapIcon.Location = myMap.Center;
mapIcon.NormalizedAnchorPoint = new Point(0.5, 1.0);
mapIcon.Title = "Here";
mapIcon.Image = mapIconStreamReference;
mapIcon.ZIndex = 0;
myMap.MapElements.Add(mapIcon);
}
At first I thought, even when the MapHoling event can't work, the Tapped action before holding should handled by MapTapped event, but it is seems this action is ignored. So remember, if a user hold the Map but not move it, nothing will happen.

SwapChainBackgroundPanel letterboxing Monogame Windows Store App

I am porting my space shooter game from Windows Phone to Windows Store App. In WP it always play in full portrait orientation.
For the Windows Store app though while in landscape mode, I want to center the game screen with letterboxing on the left and right. The problem is I can't adjust the margin property of SwapChainBackgroundPanel so the game always aligned to the left and the black screen is on the right.
Here's my code
public Game1()
{
graphics = new GraphicsDeviceManager(this);
GamePage.Current.SizeChanged += OnWindowSizeChanged;
Content.RootDirectory = "Content";
}
private void OnWindowSizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
{
var CurrentViewState = Windows.UI.ViewManagement.ApplicationView.Value;
double width = e.NewSize.Width;
double height = e.NewSize.Height;
// using Windows.Graphics.Display;
ResolutionScale resolutionScale = DisplayProperties.ResolutionScale;
string orientation = null;
if (ApplicationView.Value == ApplicationViewState.FullScreenLandscape)
{
orientation = "FullScreenLandscape";
//Does not work because it's start on the center of the screen
//Black screen is on the left and place the game screen on the right
GamePage.Current.HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Center;
//Gives error - WinRT information: Setting 'Margin' property is
//not supported on SwapChainBackgroundPanel.
GamePage.Current.Margin = new Thickness(centerMargin, 0, 0, 0);
}
else if (ApplicationView.Value == ApplicationViewState.FullScreenPortrait)
{
orientation = "FullScreenPortrait";
}
else if (ApplicationView.Value == ApplicationViewState.Filled)
{
orientation = "Filled";
}
else if (ApplicationView.Value == ApplicationViewState.Snapped)
{
orientation = "Snapped";
}
Debug.WriteLine("{0} x {1}. Scale: {2}. Orientation: {3}",
width.ToString(), height.ToString(), resolutionScale.ToString(),
orientation);
}
The GamePage.xaml is the default
<SwapChainBackgroundPanel
x:Class="SpaceShooterXW8.GamePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SpaceShooterXW8"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
</SwapChainBackgroundPanel>
After some researched I think I've figured it out thanks to this blog post. To those who are in a similar situation, here's what I did.
The beauty of the solution is that the letterboxing is automatically managed by the Resolution class. All I have to do is update the batch.begin() lines in my code to something like
batch.Begin(SpriteSortMode.Deferred,
null, SamplerState.LinearClamp,
null,
null,
null,
Resolution.getTransformationMatrix());
To handle resolution changes as the orientation changed I use this in my Game1.cs
public Game1()
{
graphics = new GraphicsDeviceManager(this);
GamePage.Current.SizeChanged += OnWindowSizeChanged;
Content.RootDirectory = "Content";
Resolution.Init(ref graphics);
Resolution.SetVirtualResolution(480, 800);
}
private void OnWindowSizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
{
var CurrentViewState = Windows.UI.ViewManagement.ApplicationView.Value;
App.AppWidth = (int)e.NewSize.Width;
App.AppHeight = (int)e.NewSize.Height;
Resolution.SetResolution(App.AppWidth, App.AppHeight, true);
}
The initial values of App.AppWidth and App.AppHeight is set in the GamePage.xaml.cs.
public GamePage(string launchArguments)
{
this.InitializeComponent();
App.AppWidth = (int)Window.Current.Bounds.Width;
App.AppHeight = (int)Window.Current.Bounds.Height;
Current = this;
// Create the game.
_game = XamlGame<Game1>.Create(launchArguments, Window.Current.CoreWindow, this);
}
Both are global static property created in the App.xaml.cs
public static int AppWidth { get; set; }
public static int AppHeight { get; set; }
The only problem I've encountered so far, the mouse input does not scale to the screen resolution change. I do not have a touch screen to test unfortunately but I think touch input should scale. If anyone tested touch, please share your findings. Thanks.
Update
I've managed to scale the Mouse input using the following
public static Vector2 ScaleGesture(Vector2 position)
{
int x = (int)(position.X / (float)App.AppWidth * (float)Screen.ScreenWidth);
int y = (int)(position.Y / (float)App.AppHeight * (float)Screen.ScreenHeight);
var scaledPosition = new Vector2(x, y);
return scaledPosition;
}

My AsyncTask does not update UI smoothly (animation)

I want to make a TextView appear little by little, like animation. The problem is, the animation is not smooth. It gets stuck for a little while sometimes and then resumes. Sometimes even worse, it goes back... I mean, the TextView gets bigger and bigger but suddenly gets smaller then bigger again. Could anyone help me?
private class UnfoldTask extends AsyncTask<Integer, Integer, Integer> {
View view;
public UnfoldTask(View v) {
this.view = v;
ViewGroup.LayoutParams pa = view.getLayoutParams();
pa.height = 0;
view.setLayoutParams(pa);
}
#Override
protected Integer doInBackground(Integer... maxHeight) {
ViewGroup.LayoutParams pa = view.getLayoutParams();
while (pa.height < maxHeight[0]) {
pa.height += (int) (24 * getResources().getDisplayMetrics().density + 0.5f);
sleep(100);
publishProgress(pa.height);
}
return maxHeight[0];
}
private void sleep(int i) {
try {
Thread.sleep(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
protected void onProgressUpdate(Integer... values) {
ViewGroup.LayoutParams pa = view.getLayoutParams();
pa.height = values[0];
view.setLayoutParams(pa);
}
#Override
protected void onPostExecute(Integer result) {
ViewGroup.LayoutParams pa = view.getLayoutParams();
pa.height = result;
view.setLayoutParams(pa);
}
}
You should be using a scale animation for this. Here's an example:
ScaleAnimation animation = new ScaleAnimation(1, 2, 1, 2, centerX, centerY); // Scales from normal size (1) to double size (2). centerX/Y is the center of your text view. Change this to set the pivot point of your animation.
animation.setDuration(1000);
myTextView.startAnimation(animation);
You can use droidQuery to simplify this:
//this will set the height of myView to 0px.
$.with(myView).height(0);
//when you are ready to animate to height (in pixels):
$.with(myView).animate("{height:" + height + "}", new AnimationOptions());
Check the documentation if you want to get fancy - such as adding duration, and event callbacks. If you are still noticing non-smooth animation, consider adding the application attribute to your AndroidManifest:
android:hardwareAccelerated="true"

Resources