I'm literally banging my head against a wall with this one.
I am creating a location based game in Unity and following all instructions from a book. We have used the standard 'google map tile' script and 'GPS location services' script that Unity provide in standard assets.
Now, when I enter play mode everything looks great but when deploying to my iPhone then no map is being rendered... only the rubbish red question marks!
Furthermore my phone isn't even being asked to ALLOW LOCATION and when I go to location services for the build there is no option for ENABLE.
Is this a setting that needs to be turned on in Xcode prior to build perhaps for location enabling?
public enum MapType
{
RoadMap,
Satellite,
Terrain,
Hybrid
}
private const string GOOGLE_MAPS_URL = "http://maps.googleapis.com/maps/api/staticmap";
public int zoomLevel = 1;
public MapType mapType = MapType.RoadMap;
public int size = 640;
public bool doubleResolution = true;
public GPSLocationService gpsLocationService;
public MapLocation worldCenterLocation;
public MapLocation tileCenterLocation;
public Vector2 TileOffset;
public Vector2 TopLeftCorner;
public Vector2 BottomRightCorner;
private double lastGPSUpdate;
// Use this for initialization
void Start ()
{
RefreshMapTile ();
}
// Update is called once per frame
void Update ()
{
if (gpsLocationService != null &&
gpsLocationService.IsServiceStarted &&
lastGPSUpdate < gpsLocationService.Timestamp)
{
lastGPSUpdate = gpsLocationService.Timestamp;
worldCenterLocation.Latitude = gpsLocationService.Latitude;
worldCenterLocation.Longitude = gpsLocationService.Longitude;
print("GoogleMapTile refreshing map texture");
RefreshMapTile();
}
}
public void RefreshMapTile() {
StartCoroutine(_RefreshMapTile());
}
IEnumerator _RefreshMapTile ()
{
tileCenterLocation.Latitude = GoogleMapUtils.adjustLatByPixels(worldCenterLocation.Latitude, (int)(size * 1 * TileOffset.y), zoomLevel);
tileCenterLocation.Longitude = GoogleMapUtils.adjustLonByPixels(worldCenterLocation.Longitude, (int)(size * 1 * TileOffset.x), zoomLevel);
var url = GOOGLE_MAPS_URL;
var queryString = "";
queryString += "center=" + WWW.UnEscapeURL (string.Format ("{0},{1}", tileCenterLocation.Latitude, tileCenterLocation.Longitude));
queryString += "&zoom=" + zoomLevel.ToString ();
queryString += "&size=" + WWW.UnEscapeURL (string.Format ("{0}x{0}", size));
queryString += "&scale=" + (doubleResolution ? "2" : "1");
queryString += "&maptype=" + mapType.ToString ().ToLower ();
queryString += "&format=" + "png";
//styles
queryString += "&style=element:geometry|invert_lightness:true|weight:3.1|hue:0x00ffd5";
queryString += "&style=element:labels|visibility:off";
var usingSensor = false;
if MOBILE_INPUT
usingSensor = Input.location.isEnabledByUser && Input.location.status == LocationServiceStatus.Running;
endif
queryString += "&sensor=" + (usingSensor ? "true" : "false");
//set map bounds rect
TopLeftCorner.x = GoogleMapUtils.adjustLonByPixels(tileCenterLocation.Longitude, -size, zoomLevel);
TopLeftCorner.y = GoogleMapUtils.adjustLatByPixels(tileCenterLocation.Latitude, size, zoomLevel);
BottomRightCorner.x = GoogleMapUtils.adjustLonByPixels(tileCenterLocation.Longitude, size, zoomLevel);
BottomRightCorner.y = GoogleMapUtils.adjustLatByPixels(tileCenterLocation.Latitude, -size, zoomLevel);
print(string.Format("Tile {0}x{1} requested with {2}", TileOffset.x, TileOffset.y, queryString));
var req = new WWW(url + "?" + queryString);
//var req = new WWW("https://maps.googleapis.com/maps/api/staticmap?center=50.917316,-114.080923&zoom=17&format=png&sensor=false&size=640x640&scale=2&maptype=roadmap&style=feature:landscape.man_made|visibility:on|invert_lightness:true");
yield return req;
GetComponent<Renderer>().material.mainTexture = req.texture;
print(string.Format("Tile {0}x{1} textured", TileOffset.x, TileOffset.y));
}
}
}
Many thanks for your help
Carl
Related
I have the below code as a GPS controller and in Unity my map tiles are texturing fine... however, when I run in Xcode I get red question marks on all tiles. Is there something I'm not seeing here?
Have checked in Xcode debugger and my location coordinates are being found correctly but just not showing on tiles
Many thanks
{
RoadMap,
Satellite,
Terrain,
Hybrid
}
private const string GOOGLE_MAPS_URL = "http://maps.googleapis.com/maps/api/staticmap";
public int zoomLevel = 1;
public MapType mapType = MapType.RoadMap;
public int size = 640;
public bool doubleResolution = true;
public GPSLocationService gpsLocationService;
public MapLocation worldCenterLocation;
public MapLocation tileCenterLocation;
public Vector2 TileOffset;
public Vector2 TopLeftCorner;
public Vector2 BottomRightCorner;
private double lastGPSUpdate;
// Use this for initialization
void Start ()
{
RefreshMapTile ();
}
// Update is called once per frame
void Update ()
{
if (gpsLocationService != null &&
gpsLocationService.IsServiceStarted &&
lastGPSUpdate < gpsLocationService.Timestamp)
{
lastGPSUpdate = gpsLocationService.Timestamp;
worldCenterLocation.Latitude = gpsLocationService.Latitude;
worldCenterLocation.Longitude = gpsLocationService.Longitude;
print("GoogleMapTile refreshing map texture");
RefreshMapTile();
}
}
public void RefreshMapTile() {
StartCoroutine(_RefreshMapTile());
}
IEnumerator _RefreshMapTile ()
{
tileCenterLocation.Latitude = GoogleMapUtils.adjustLatByPixels(worldCenterLocation.Latitude, (int)(size * 1 * TileOffset.y), zoomLevel);
tileCenterLocation.Longitude = GoogleMapUtils.adjustLonByPixels(worldCenterLocation.Longitude, (int)(size * 1 * TileOffset.x), zoomLevel);
var url = GOOGLE_MAPS_URL;
var queryString = "";
queryString += "center=" + WWW.UnEscapeURL (string.Format ("{0},{1}", tileCenterLocation.Latitude, tileCenterLocation.Longitude));
queryString += "&zoom=" + zoomLevel.ToString ();
queryString += "&size=" + WWW.UnEscapeURL (string.Format ("{0}x{0}", size));
queryString += "&scale=" + (doubleResolution ? "2" : "1");
queryString += "&maptype=" + mapType.ToString ().ToLower ();
queryString += "&format=" + "png";
//styles
queryString += "&style=element:geometry|invert_lightness:true|weight:3.1|hue:0x00ffd5";
queryString += "&style=element:labels|visibility:off";
var usingSensor = false;
if MOBILE_INPUT
usingSensor = Input.location.isEnabledByUser && Input.location.status == LocationServiceStatus.Running;
endif
queryString += "&sensor=" + (usingSensor ? "true" : "false");
//set map bounds rect
TopLeftCorner.x = GoogleMapUtils.adjustLonByPixels(tileCenterLocation.Longitude, -size, zoomLevel);
TopLeftCorner.y = GoogleMapUtils.adjustLatByPixels(tileCenterLocation.Latitude, size, zoomLevel);
BottomRightCorner.x = GoogleMapUtils.adjustLonByPixels(tileCenterLocation.Longitude, size, zoomLevel);
BottomRightCorner.y = GoogleMapUtils.adjustLatByPixels(tileCenterLocation.Latitude, -size, zoomLevel);
print(string.Format("Tile {0}x{1} requested with {2}", TileOffset.x, TileOffset.y, queryString));
var req = new WWW(url + "?" + queryString);
//var req = new WWW("https://maps.googleapis.com/maps/api/staticmap?center=50.917316,-114.080923&zoom=17&format=png&sensor=false&size=640x640&scale=2&maptype=roadmap&style=feature:landscape.man_made|visibility:on|invert_lightness:true");
yield return req;
GetComponent<Renderer>().material.mainTexture = req.texture;
print(string.Format("Tile {0}x{1} textured", TileOffset.x, TileOffset.y));
}
}
So I'm playing around with the BouncyGame. I made it so that when you start the game you need to press the screen for it to start. I would like to implement this whenever you play a new round as well. I tried to reuse this att the bottom of my code but it made it extremely laggy.
// Register for touch events
var touchListener = new CCEventListenerTouchAllAtOnce();
touchListener.OnTouchesEnded = OnTouchesEnded;
touchListener.OnTouchesMoved = OnTouchesEnded;
AddEventListener(touchListener, this);
}
void OnTouchesEnded(List<CCTouch> touches, CCEvent touchEvent)
{
if (touches.Count > 0)
{
Schedule(RunGameLogic);
scoreLabel.Text = "Score: 0";
paddleSprite.RunAction(new CCMoveTo(.1f, new CCPoint(touches[0].Location.X, paddleSprite.PositionY)));
}
}
I have no idea how to do this, tried for 2 hours with 0 results. Any suggestions are welcome.
Here's the full code.
using System;
using System.Collections.Generic;
using CocosSharp;
using Microsoft.Xna.Framework;
namespace CocosSharpGameTest
{
public class IntroLayer : CCLayerColor
{
// Define a label variable
CCLabel scoreLabel;
CCSprite paddleSprite, ballSprite;
public IntroLayer() : base(CCColor4B.Black)
{
// create and initialize a Label
scoreLabel = new CCLabel("Tap to GO!", "Arial", 80, CCLabelFormat.SystemFont);
// add the label as a child to this Layer
scoreLabel.PositionX = 50;
scoreLabel.PositionY = 1000;
scoreLabel.AnchorPoint = CCPoint.AnchorUpperLeft;
AddChild(scoreLabel);
paddleSprite = new CCSprite("paddle.png");
AddChild(paddleSprite);
ballSprite = new CCSprite("ball.png");
AddChild(ballSprite);
}
protected override void AddedToScene()
{
base.AddedToScene();
// Use the bounds to layout the positioning of our drawable assets
CCRect bounds = VisibleBoundsWorldspace;
// position the label on the center of the screen
paddleSprite.PositionX = 100;
paddleSprite.PositionY = 100;
ballSprite.PositionX = 320;
ballSprite.PositionY = 640;
// Register for touch events
var touchListener = new CCEventListenerTouchAllAtOnce();
touchListener.OnTouchesEnded = OnTouchesEnded;
touchListener.OnTouchesMoved = OnTouchesEnded;
AddEventListener(touchListener, this);
}
void OnTouchesEnded(List<CCTouch> touches, CCEvent touchEvent)
{
if (touches.Count > 0)
{
Schedule(RunGameLogic);
scoreLabel.Text = "Score: 0";
paddleSprite.RunAction(new CCMoveTo(.1f, new CCPoint(touches[0].Location.X, paddleSprite.PositionY)));
}
}
float ballXVelocity;
float ballYVelocity;
// How much to modify the ball's y velocity per second:
const float gravity = 140;
int score = 0;
void RunGameLogic(float frameTimeInSeconds)
{
// This is a linear approximation, so not 100% accurate
ballYVelocity += frameTimeInSeconds * -gravity;
ballSprite.PositionX += ballXVelocity * frameTimeInSeconds;
ballSprite.PositionY += ballYVelocity * frameTimeInSeconds;
bool overlap = ballSprite.BoundingBoxTransformedToParent.IntersectsRect(paddleSprite.BoundingBoxTransformedToParent);
bool movingDown = ballYVelocity < 0;
if (overlap && movingDown)
{
ballYVelocity *= -1;
const float minXVelocity = -300;
const float maxXVelocity = 300;
ballXVelocity = CCRandom.GetRandomFloat(minXVelocity, maxXVelocity);
score++;
scoreLabel.Text = "Score: " + score;
}
float ballRight = ballSprite.BoundingBoxTransformedToParent.MaxX;
float ballLeft = ballSprite.BoundingBoxTransformedToParent.MinX;
float screenRight = VisibleBoundsWorldspace.MaxX;
float screenLeft = VisibleBoundsWorldspace.MinX;
bool shouldReflectXVelocity =
(ballRight > screenRight && ballXVelocity > 0) ||
(ballLeft < screenLeft && ballXVelocity < 0);
if (shouldReflectXVelocity)
{
ballXVelocity *= -1;
}
if (ballSprite.PositionY < VisibleBoundsWorldspace.MinY)
{
ballSprite.PositionX = 320;
ballSprite.PositionY = 640;
ballXVelocity = 0;
ballYVelocity = 0;
ballYVelocity *= -1;
scoreLabel.Text = "Score: 0";
score = 0;
}
}
}
}
Thanks in advance!
Figured it out!
There is an "Unschedule" Method built into Cocossharp.
Ref. https://developer.xamarin.com/api/namespace/CocosSharp/
I just added
Unschedule(RunGameLogic);
at the very en of my RunGameLogic method under
if (ballSprite.PositionY < VisibleBoundsWorldspace.MinY)
So once the ballSprite is out of bounds it will Unschedule what i Scheduled in my OntouchesEnded method. That means the code goes back to listening for touches.
Might have made some errors, but this is as best I could figure it out and it works!
I am trying to run some source code that I downloaded from here
This is essentially an app that will allow users to manage contacts and calendar appointments etc.
I am currently experiencing 3 errors, all seemingly related...
CS1061 - UIImageView does not contain a definition for 'SetImage' and no extension method 'SetImage' accepting a first argument of type 'UIImageView' could be found (are you missing a using directive or an assembly reference?)
It seems that SetImage is not defined somewhere?
Would someone be able to tell me what i need to do to resolve the error. I post my code below...
using System;
using Foundation;
using UIKit;
using System.CodeDom.Compiler;
using MessageUI;
using Microsoft.Office365.OutlookServices;
using FiveMinuteMeeting.Shared.ViewModels;
using CoreGraphics;
using FiveMinuteMeeting.Shared;
namespace FiveMinuteMeeting.iOS
{
partial class ContactDetailViewController : UIViewController
{
public ContactDetailViewController(IntPtr handle)
: base(handle)
{
}
public DetailsViewModel ViewModel
{
get;
set;
}
UIBarButtonItem save;
public override void ViewDidLoad()
{
base.ViewDidLoad();
NavigationController.NavigationBar.BarStyle = UIBarStyle.Black;
save = new UIBarButtonItem(UIBarButtonSystemItem.Save,
async (sender, args) =>
{
ViewModel.FirstName = TextFirst.Text.Trim();
ViewModel.LastName = TextLast.Text.Trim();
ViewModel.Email = TextEmail.Text.Trim();
ViewModel.Phone = TextPhone.Text.Trim();
//BigTed.BTProgressHUD.Show("Saving contact...");
await ViewModel.SaveContact();
//BigTed.BTProgressHUD.Dismiss();
NavigationController.PopToRootViewController(true);
});
TextEmail.ShouldReturn += ShouldReturn;
TextFirst.ShouldReturn += ShouldReturn;
TextPhone.ShouldReturn += ShouldReturn;
TextLast.ShouldReturn += ShouldReturn;
TextEmail.ValueChanged += (sender, args) =>
{
ImagePhoto.SetImage(
url: new NSUrl(Gravatar.GetURL(TextEmail.Text, 172)),
placeholder: UIImage.FromBundle("missing.png")
);
};
var color = new CGColor(17.0F / 255.0F, 113.0F / 255.0F, 197.0F / 255F);
TextEmail.Layer.BorderColor = color;
TextFirst.Layer.BorderColor = color;
TextPhone.Layer.BorderColor = color;
TextLast.Layer.BorderColor = color;
ButtonCall.Clicked += (sender, args) => PlaceCall();
NSNotificationCenter.DefaultCenter.AddObserver
(UIKeyboard.DidShowNotification, KeyBoardUpNotification);
// Keyboard Down
NSNotificationCenter.DefaultCenter.AddObserver
(UIKeyboard.WillHideNotification, KeyBoardDownNotification);
double min = Math.Min((float)ImagePhoto.Frame.Width, (float)ImagePhoto.Frame.Height);
ImagePhoto.Layer.CornerRadius = (float)(min / 2.0);
ImagePhoto.Layer.MasksToBounds = false;
ImagePhoto.Layer.BorderColor = new CGColor(1, 1, 1);
ImagePhoto.Layer.BorderWidth = 3;
ImagePhoto.ClipsToBounds = true;
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
if (ViewModel == null)
{
ViewModel = new DetailsViewModel();
NavigationItem.RightBarButtonItem = save;
}
else
{
this.Title = ViewModel.FirstName;
TextEmail.Text = ViewModel.Email;
TextFirst.Text = ViewModel.FirstName;
TextLast.Text = ViewModel.LastName;
TextPhone.Text = ViewModel.Phone;
ImagePhoto.SetImage(
url: new NSUrl(Gravatar.GetURL(ViewModel.Contact.EmailAddresses[0].Address, 172)),
placeholder: UIImage.FromBundle("missing.png")
);
NavigationItem.RightBarButtonItem = null;
}
}
private bool ShouldReturn(UITextField field)
{
field.ResignFirstResponder();
return true;
}
private void PlaceCall()
{
var alertPrompt = new UIAlertView("Dial Number?",
"Do you want to call " + TextPhone.Text + "?",
null, "No", "Yes");
alertPrompt.Dismissed += (sender, e) =>
{
if ((int)e.ButtonIndex >= (int)alertPrompt.FirstOtherButtonIndex)
{
var url = new NSUrl("tel:" + TextPhone.Text);
if (!UIApplication.SharedApplication.OpenUrl(url))
{
var av = new UIAlertView("Not supported",
"Scheme 'tel:' is not supported on this device",
null,
"OK",
null);
av.Show();
}
else
{
UIApplication.SharedApplication.OpenUrl(url);
}
}
};
alertPrompt.Show();
}
/*private async void SendEmail()
{
var mailController = new MFMailComposeViewController();
mailController.SetToRecipients(new string[] { TextEmail.Text });
mailController.SetSubject("5 Minute Meeting");
mailController.SetMessageBody("We are having a 5 minute stand up tomorrow at this time! Check your calendar.", false);
mailController.Finished += (object s, MFComposeResultEventArgs args) =>
{
Console.WriteLine(args.Result.ToString());
args.Controller.DismissViewController(true, (Action)null);
};
PresentViewControllerAsync(mailController, true);
}*/
public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
{
switch(segue.Identifier)
{
case "email":
{
var vc = segue.DestinationViewController as SendEmailViewController;
vc.ViewModel.FirstName = ViewModel.FirstName;
vc.ViewModel.LastName = ViewModel.LastName;
vc.ViewModel.Email = ViewModel.Email;
}
break;
case "meeting":
{
var vc = segue.DestinationViewController as NewEventDurationViewController;
vc.ViewModel.FirstName = ViewModel.FirstName;
vc.ViewModel.LastName = ViewModel.LastName;
vc.ViewModel.Email = ViewModel.Email;
}
break;
}
}
#region Keyboard
private UIView activeview; // Controller that activated the keyboard
private float scrollamount; // amount to scroll
private float bottom; // bottom point
private const float Offset = 68.0f; // extra offset
private bool moveViewUp; // which direction are we moving
private void KeyBoardDownNotification(NSNotification notification)
{
if (moveViewUp) { ScrollTheView(false); }
}
private void ScrollTheView(bool move)
{
// scroll the view up or down
UIView.BeginAnimations(string.Empty, System.IntPtr.Zero);
UIView.SetAnimationDuration(0.3);
CGRect frame = (CGRect)View.Frame;
if (move)
{
frame.Y -= scrollamount;
}
else
{
frame.Y += scrollamount;
scrollamount = 0;
}
View.Frame = frame;
UIView.CommitAnimations();
}
private void KeyBoardUpNotification(NSNotification notification)
{
// get the keyboard size
var r = (CGRect)UIKeyboard.FrameBeginFromNotification((NSNotification)notification);
// Find what opened the keyboard
foreach (UIView view in this.View.Subviews)
{
if (view.IsFirstResponder)
activeview = view;
}
// Bottom of the controller = initial position + height + offset
bottom = ((float)activeview.Frame.Y + (float)activeview.Frame.Height + Offset);
// Calculate how far we need to scroll
scrollamount = ((float)r.Height - ((float)View.Frame.Size.Height - bottom));
// Perform the scrolling
if (scrollamount > 0)
{
moveViewUp = true;
ScrollTheView(moveViewUp);
}
else
{
moveViewUp = false;
}
}
#endregion
}
}
I am very new to Xamarin & app development and I just wanted to run this because it is a very similar project to one that I am creating.
Thanks for your help
SetImage is a extension method contained in Xamarin.SDWebImage. Ensure, that you have restored all nuget packages or have installed it via
Install-Package Xamarin.SDWebImage
see: https://www.nuget.org/packages/Xamarin.SDWebImage/
Here a explanation of the error.
https://msdn.microsoft.com/en-us/library/bb383961.aspx
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
CS1061 - UIImageView does not contain a definition for 'SetImage' and no extension method 'SetImage' accepting a first argument of type 'UIImageView' could be found (are you missing a using directive or an assembly reference?)
In your code I don't watch a declaration of ImagePhoto check right click goto definition. And verify that for imagePhoto exist a method setImage ImagePhoto I'm asumming that is object of type UIImage
ImagePhoto.SetImage(
url: new NSUrl(Gravatar.GetURL(TextEmail.Text, 172)),
placeholder: UIImage.FromBundle("missing.png")
);
I made a simulation in OpenSimulator. I changed the terrain to resemble that of Atlantis and used the OpenSimTide module to have the tide cause the disappearance of Atlantis.
Now the OpenSimTide module works, but for some reason I just can't find out how I make the terrain go lower whenever the tide reaches a specific level. How can I achieve this?
Here's what i have up until now:
using System;
using System.Net;
using System.IO;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using log4net;
using Nini.Config;
using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenMetaverse;
using Mono.Addins;
[assembly: Addin("OpenSimTide", "0.2")]
[assembly: AddinDependency("OpenSim.Region.Framework",
OpenSim.VersionInfo.VersionNumber)]
namespace TideModule
{
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "OpenSimTide")]
public class OpenSimTide : INonSharedRegionModule
{
#region Fields
private static readonly ILog m_log = LogManager.GetLogger (MethodBase.GetCurrentMethod ().DeclaringType);
public string Name { get { return m_name; } }
public Type ReplaceableInterface { get { return null; } }
private const int TICKS_PER_SECOND = 10000000;
public string m_name = "OpenSimTide";
private string m_regionConfigDir = "";
private uint m_frame = 0;
private int m_frameUpdateRate = 100;
private bool m_enabled = false;
private bool m_ready = false;
private float m_tideLevel = 20.0f; //current water level in m
private float m_lowTide = 18.0f; //low water level in m
private float m_highTide = 22.0f; //high water level in m
private ulong m_cycleTime = 3600; //low->high->low time in seconds
private DateTime m_lowTideTime = new DateTime(); // datetime indicating when next low tide will be
private DateTime m_highTideTime = new DateTime(); // datetime indicating when next hightide will be
private bool m_tideDirection = true; // which direction is the tide travelling, 'Coming In'(true) or 'Going Out'(false)
private bool m_lastTideDirection = true;
private bool m_tideInfoDebug = false; //do we chat the tide to the OpenSim console?
private bool m_tideInfoBroadcast = true; //do we chat the tide to the region?
private int m_tideInfoChannel = 5555; //chat channel for all tide info
private int m_tideLevelChannel = 5556; //chat channel for just the tide level in m
private int m_tideAnnounceCount = 5; //how many times do we announce the turning tide
private int m_tideAnnounceCounter = 0; //counter we use to count announcements of low or high tide
private string m_tideAnnounceMsg = "";
private ITerrainChannel m_channel;
public scene m_scene;
public IConfigSource m_config;
public RegionInfo m_regionInfo;
public Dictionary<string, Scene> mScene = new Dictionary<string, Scene> ();
public Vector3 m_shoutPos = new Vector3(128f, 128f, 30f);
#endregion
#region IRegionModuleBase implementation
public void Initialise (IConfigSource source)
{
m_config = source;
}
public void Close ()
{
if (m_enabled) {
m_scene.EventManager.OnFrame -= TideUpdate;
}
}
public void AddRegion (Scene scene)
{
IConfig cnf;
m_log.InfoFormat("[{0}]: Adding region '{1}' to this module", m_name, scene.RegionInfo.RegionName);
cnf = m_config.Configs["Startup"];
m_regionConfigDir = cnf.GetString("regionload_regionsdir", Path.Combine(Util.configDir(), "bin/Regions/"));
cnf = m_config.Configs[scene.RegionInfo.RegionName];
if(cnf == null)
{
m_log.InfoFormat("[{0}]: No region section [{1}] found in addon-modules/{2}/config/*.ini configuration files.", m_name, scene.RegionInfo.RegionName, m_name);
//string moduleConfigFile = Path.Combine(Util.configDir(),m_name + ".ini");
string moduleConfigFile = Path.Combine(m_regionConfigDir, "Regions.ini");
try
{
m_log.InfoFormat("[{0}]: Checking {1} for [{2}] section containing valid config keys", m_name, moduleConfigFile, scene.RegionInfo.RegionName);
m_config = new IniConfigSource(moduleConfigFile);
cnf = m_config.Configs[scene.RegionInfo.RegionName];
}
catch (Exception)
{
cnf = null;
}
if (cnf == null)
{
m_log.InfoFormat("[{0}]: No region section [{1}] found in configuration {2}. Tide in this region is set to Disabled", m_name, scene.RegionInfo.RegionName, moduleConfigFile);
m_enabled = false;
return;
}
}
m_enabled = cnf.GetBoolean("TideEnabled", false);
if (m_enabled)
{
m_frameUpdateRate = cnf.GetInt("TideUpdateRate", 150);
m_lowTide = cnf.GetFloat("TideLowWater", 18.0f);
m_highTide = cnf.GetFloat("TideHighWater", 22.0f);
m_cycleTime = (ulong)cnf.GetInt("TideCycleTime", 3600);
m_tideInfoDebug = cnf.GetBoolean("TideInfoDebug", false);
m_tideInfoBroadcast = cnf.GetBoolean("TideInfoBroadcast", true);
m_tideInfoChannel = cnf.GetInt("TideInfoChannel", 5555);
m_tideLevelChannel = cnf.GetInt("TideLevelChannel", 5556);
m_tideAnnounceCount = cnf.GetInt("TideAnnounceCount", 5);
m_log.InfoFormat("[{0}]: Enabled with an update rate every {1} frames, Low Water={2}m, High Water={3}m, Cycle Time={4} secs", m_name, m_frameUpdateRate, m_lowTide, m_highTide, m_cycleTime);
m_log.InfoFormat("[{0}]: Info Channel={1}, Water Level Channel={2}, Info Broadcast is {3}, Announce Count={4}", m_name, m_tideInfoChannel, m_tideLevelChannel, m_tideInfoBroadcast, m_tideAnnounceCounter);
m_frame = 0;
m_ready = true; // Mark Module Ready for duty
m_shoutPos = new Vector3(scene.RegionInfo.RegionSizeX / 2f, scene.RegionInfo.RegionSizeY / 2f, 30f);
scene.EventManager.OnFrame += TideUpdate;
m_scene = scene;
}
else
{
m_log.InfoFormat("[{0}]: Tide in this region is set to Disabled", m_name);
}
}
public void RemoveRegion (Scene scene)
{
m_log.InfoFormat("[{0}]: Removing region '{1}' from this module", m_name, scene.RegionInfo.RegionName);
if (m_enabled)
{
scene.EventManager.OnFrame -= TideUpdate;
}
}
public void RegionLoaded (Scene scene)
{
}
#endregion
#region TideModule
// Place your methods here
public void TideUpdate ()
{
ulong timeStamp;
double cyclePos; //cycles from 0.0000000001 to 0.999999999999
double cycleRadians;
double tideRange;
double tideMiddle;
string tideLevelMsg;
if (((m_frame++ % m_frameUpdateRate) != 0) || !m_ready) {
return;
}
timeStamp = (ulong) (DateTime.Now.Ticks);
cyclePos = (double)(timeStamp % (m_cycleTime * TICKS_PER_SECOND)) / (m_cycleTime * TICKS_PER_SECOND);
cycleRadians = cyclePos * Math.PI * 2;
if (cyclePos < 0.5) m_tideDirection = false; else m_tideDirection = true;
if (m_tideDirection != m_lastTideDirection)
{ //if the tide changes re-calculate the tide times
if (cyclePos < 0.5)
{ // tide just changed to be high->low
m_lowTideTime = DateTime.Now.AddSeconds((double)(m_cycleTime * (0.5 - cyclePos)));
m_highTideTime = m_lowTideTime.AddSeconds((double)(m_cycleTime / 2));
m_tideAnnounceMsg = "High Tide";
}
else
{ //tide just changed to be low->high
m_highTideTime = DateTime.Now.AddSeconds((double)(m_cycleTime * (1.0 - cyclePos)));
m_lowTideTime = m_highTideTime.AddSeconds((double)(m_cycleTime / 2));
m_tideAnnounceMsg = "Low Tide";
}
m_lastTideDirection = m_tideDirection;
}
tideRange = (double) (m_highTide - m_lowTide) / 2;
tideMiddle = (double) m_lowTide + tideRange;
m_tideLevel = (float) (Math.Cos(cycleRadians) * tideRange + tideMiddle);
tideLevelMsg = "Current Server Time: " + DateTime.Now.ToString("T") + "\n";
tideLevelMsg += "Current Tide Level: " + m_tideLevel.ToString() + "\n";
tideLevelMsg += "Low Tide Time: " + m_lowTideTime.ToString("T") + "\n";
tideLevelMsg += "Low Tide Level: " + m_lowTide.ToString() + "\n";
tideLevelMsg += "High Tide Time: " + m_highTideTime.ToString("T") + "\n";
tideLevelMsg += "High Tide Level: " + m_highTide.ToString() + "\n";
tideLevelMsg += "Tide Direction: " + ((m_tideDirection) ? "Coming In" : "Going Out") + "\n";
tideLevelMsg += "Cycle Position: " + cyclePos.ToString() + "\n";
if (m_tideAnnounceMsg != "")
{
if (m_tideAnnounceCounter++ > m_tideAnnounceCount)
{
m_tideAnnounceCounter = 0;
m_tideAnnounceMsg = "";
}
else
{
tideLevelMsg += "Tide Warning: " + m_tideAnnounceMsg + "\n";
}
}
if (m_tideInfoDebug) m_log.InfoFormat("[{0}]: Sea Level currently at {1}m in Region: {2}", m_name, m_tideLevel, m_scene.RegionInfo.RegionName);
if (m_tideInfoBroadcast && m_tideDirection)
{
m_scene.SimChatBroadcast(Utils.StringToBytes(tideLevelMsg), ChatTypeEnum.Region, m_tideInfoChannel, m_shoutPos, "TIDE", UUID.Zero, false);
m_scene.SimChatBroadcast(Utils.StringToBytes(m_tideLevel.ToString()), ChatTypeEnum.Region, m_tideLevelChannel, m_shoutPos, "TIDE", UUID.Zero, false);
}
if (m_tideInfoDebug) m_log.InfoFormat("[{0}]: Updating Region: {1}", m_name, m_scene.RegionInfo.RegionName);
m_scene.RegionInfo.RegionSettings.WaterHeight = m_tideLevel;
m_scene.EventManager.TriggerRequestChangeWaterHeight(m_tideLevel);
m_scene.EventManager.TriggerTerrainTick();
if (m_tideInfoBroadcast && !m_tideDirection)
{
m_scene.SimChatBroadcast(Utils.StringToBytes(tideLevelMsg), ChatTypeEnum.Region, m_tideInfoChannel, m_shoutPos, "TIDE", UUID.Zero, false);
m_scene.SimChatBroadcast(Utils.StringToBytes(m_tideLevel.ToString()), ChatTypeEnum.Region, m_tideLevelChannel, m_shoutPos, "TIDE", UUID.Zero, false);
}
}
private void InterfaceMultiplyTerrain(Object[] args)
{
int x, y;
for (x = 0; x < m_channel.Width; x++)
for (y = 0; y < m_channel.Height; y++)
m_channel[x, y] *= (double)args[0];
}
public void UpdateTerrainWithTide()
{
float terrain_scale = 0.75 * m_channel.Height;
float terrain_scale2 = 0.95 * m_channel.Height;
while (tideDirection == true)
{
if (m_tideLevel > terrain_scale && m_tideLevel < terrain_scale2)
{
InterfaceMultiplyTerrain(0.95);
}
}
}
#endregion
}
}
Goal
Get a gif working in unity from a URL, I am currently using the WWW class. I am currently getting a byte[] and converting it to a System.Drawing.Image. This works in the editor but not any build
Error:
"Type Load Exception: Could not load type" System.IO.InternalBufferOverflowException from the assembly "System.Drawing.Image" at line 111
Why?
It has to do with the System.Drawing.Image.FromStream built in method, Unity for some reason doesn't like it. The other options are .FromFile and .FromHBitMap, I dont know how to use HBitMap but going back to my original plan, .FromFile is unusable to me.
Entire Code
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using UnityEngine;
using System.IO;
using UnityEngine.UI;
using System.Collections;
public class AnimatedGifDrawerBack : MonoBehaviour
{
public string loadingGifPath;
public float speed = 1;
public Vector2 drawPosition;
public string pName;
public float width;
public float height;
public float percentage;
public GameObject positionPlaceHolderGO;
public Vector2 positionPlaceHolder;
public Text debugText;
private SpriteImageArray sia;
private string url;
private WWW www;
public bool finishedWWW = false;
public bool hasWWW = false;
public bool canOnGUI = false;
List<Texture2D> gifFrames = new List<Texture2D>();
void Start()
{
percentage = 1.3f;
positionPlaceHolderGO = GameObject.FindGameObjectWithTag("PBLPlace");
positionPlaceHolder = positionPlaceHolderGO.transform.position;
}
void Update()
{
while (hasWWW == false)
{
Debug.Log("in while loop");
if (this.GetComponent<PokemonCreatorBack>().name == "")
{
}
else
{
debugText.text = "Name Found";
url = "www.pkparaiso.com/imagenes/xy/sprites/animados-espalda/" + this.GetComponent<PokemonCreatorBack>().PokemonName.ToLower() + ".gif";
StartCoroutine(WaitForRequest(positionPlaceHolderGO, url));
hasWWW = true;
debugText.text = "hawWWW = true";
}
}
}
void OnGUI()
{
height = (float)Screen.height - 80f / percentage;
//GUI.DrawTexture (new Rect (Screen.width-width, Screen.height - height, gifFrames [0].width * percentage, gifFrames [0].height * percentage), gifFrames [(int)(Time.frameCount * speed) % gifFrames.Count]);
if (canOnGUI)
GUI.DrawTexture(new Rect(positionPlaceHolder.x, positionPlaceHolder.y, gifFrames[0].width * percentage, gifFrames[0].height * percentage), gifFrames[(int)(Time.frameCount * speed) % gifFrames.Count]);
}
IEnumerator WaitForRequest(GameObject go, string url)
{
www = new WWW(url);
yield return www;
if (www.error == null)
{
Debug.Log("WWW Ok!: " + www.texture.name);
}
else
{
Debug.Log("WWW Error: " + www.error);
}
debugText.text = "finishedWWW = true";
finishedWWW = true;
}
public System.Drawing.Image ByteArrayToImage(byte[] byteArrayIn)
{
if (finishedWWW == false)
{
Debug.Log("Called too early");
}
if (byteArrayIn == null)
{
Debug.Log("Null byte array");
return null;
}
Debug.Log("Bytra array in length: " + byteArrayIn.GetLongLength(0));
MemoryStream ms = new MemoryStream(byteArrayIn);
System.Drawing.Image returnImage = System.Drawing.Image.FromStream(ms); //MAIN SOURCE OF ERROR HERE
finishedWWW = true;
debugText.text = "System.Image Created";
return returnImage;
}
public void loadImage()
{
Debug.Log("Called Load Image BACK");
debugText.text = "Called Load Image BACK";
System.Drawing.Image gifImage = ByteArrayToImage(www.bytes);
FrameDimension dimension = new FrameDimension(gifImage.FrameDimensionsList[0]);
int frameCount = gifImage.GetFrameCount(dimension);
for (int i = 0; i < frameCount; i++)
{
gifImage.SelectActiveFrame(dimension, i);
Bitmap frame = new Bitmap(gifImage.Width, gifImage.Height);
System.Drawing.Graphics.FromImage(frame).DrawImage(gifImage, Point.Empty);
Texture2D frameTexture = new Texture2D(frame.Width, frame.Height);
for (int x = 0; x < frame.Width; x++)
for (int y = 0; y < frame.Height; y++)
{
System.Drawing.Color sourceColor = frame.GetPixel(x, y);
frameTexture.SetPixel(frame.Width - 1 + x, -y, new Color32(sourceColor.R, sourceColor.G, sourceColor.B, sourceColor.A)); // for some reason, x is flipped
}
frameTexture.Apply();
gifFrames.Add(frameTexture);
}
Debug.Log("Starting ON GUI!");
debugText.text = "Starting OnGUI";
canOnGUI = true;
}
}
Thoughts
byteArrayIn.GetLongLength(0)
returns 80,000 at most.
The last debug statement coming through is Called Image Loading BACK.
I will write my own fire streamer if necessary, and if it is necessary can someone point me in the write direction for that.
I think the main workaround is dealing with the Image.FromStream().
There are two of these in the scene.
All thoughts or solutions are welcome, I really just wish I knew how to tackle this error so that I could share it more with the Unity Community.
We faced the same problem this morning.
The app is not finding a type in the System.IO namespace required by System.Drawing.Image.
The missing type has apparently been stripped from the system.dll that is packed during the build process.
To fix this, you need to copy and replace the unity generated System.dll with the original mono System.dll.
In your build, replace projectName_Data\Managed\System.dll with the System.dll found in Unity's mono installation folder:
Editor\Data\Mono\lib\mono\2.0 (relative to the root of the Unity installation folder).
Hope it helps!