I'm learning F#, and decided to try making simple XNA games for windows using F# (pure enthusiasm) , and got a window with some images showing up.
Here's the code:
(*Methods*)
member self.DrawSprites() =
_spriteBatch.Begin()
for i = 0 to _list.Length-1 do
let spentity = _list.List.ElementAt(i)
_spriteBatch.Draw(spentity.ImageTexture,new Rectangle(100,100,(int)spentity.Width,(int)spentity.Height),Color.White)
_spriteBatch.End()
(*Overriding*)
override self.Initialize() =
ChangeGraphicsProfile()
_graphicsDevice <- _graphics.GraphicsDevice
_list.AddSprite(0,"NagatoYuki",992.0,990.0)
base.Initialize()
override self.LoadContent() =
_spriteBatch <- new SpriteBatch(_graphicsDevice)
base.LoadContent()
override self.Draw(gameTime : GameTime) =
base.Draw(gameTime)
_graphics.GraphicsDevice.Clear(Color.CornflowerBlue)
self.DrawSprites()
And the AddSprite Method:
member self.AddSprite(ID : int,imageTexture : string , width : float, height : float) =
let texture = content.Load<Texture2D>(imageTexture)
list <- list # [new SpriteEntity(ID,list.Length, texture,Vector2.Zero,width,height)]
The _list object has a ContentManager, here's the constructor:
type SpriteList(_content : ContentManager byref) =
let mutable content = _content
let mutable list = []
But I can't minimize the window, since when it regains its focus, i get this error:
ObjectDisposedException
Cannot access a disposed object.
Object name: 'GraphicsDevice'.
What is happening?
Well after struggling for some time I got it to work. But it doesn't seem "right"
(thinking that way, using XNA and F# doesn't seem right either, but it's fun.)
(*Methods*)
member self.DrawSprites() =
_spriteBatch.Begin()
for i = 0 to _list.Length-1 do
let spentity = _list.List.ElementAt(i)
if spentity.ImageTexture.IsDisposed then
spentity.ImageTexture <- _list.Content.Load<Texture2D>(spentity.Name)
_spriteBatch.Draw(spentity.ImageTexture,new Rectangle(100,100,(int)spentity.Width,(int)spentity.Height),Color.White)
_spriteBatch.End()
(*Overriding*)
override self.Initialize() =
ChangeGraphicsProfile()
_list.AddSprite(0,"NagatoYuki",992.0,990.0)
base.Initialize()
override self.LoadContent() =
ChangeGraphicsProfile()
_graphicsDevice <- _graphics.GraphicsDevice
_spriteBatch <- new SpriteBatch(_graphicsDevice)
base.LoadContent()
I adjust the graphicsDevice whenever my game needs to LoadContent, and in the DrawSprites() method I check if the texture is disposed, if it is, load it up again.
But this thing bugs me. I didn't know I had to Load all Content again everytime the window is minimized.
(And the code makes it look like Initialize() loads Content, and LoadContent() initializes, but oh well)
What you are observing is normal behaviour, it's by the way not specific to F#. See http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.game.loadcontent.aspx
This method is called by Initialize. Also, it is called any time the game content needs to be reloaded, such as when the DeviceReset event occurs.
Are you loading all of your content in Game.LoadContent? If you do, you should not be getting these errors.
Related
I currently have a function that, when a button is pressed, takes a value that is determined from a UIStepper and adds it to a list of numbers. When I press the Add Tip button, it correctly displays the tip amount in the text view, but when I add a new value it replaces it rather than adding it underneath.
Here is the function:
#IBAction func addTipButton(_ sender: UIButton) {
let tipDollarCent = dollar + cent
sampleLog.text = "\(tipDollarCent)\n"
totalLabel.text = tipDollarCent
}
sampleLog is the Text View that needs to take a variable amount of lines of data, depending on how many time the user presses addTipButton
I am aware that my best course of action is probably to do an incremental loop, and I have tried implementing a separate addNewLine function, but tipDollarCent was out of scope and gave me an error.
I also initially tried adding sampleLog.text = "\(tipDollarCent)\n" += "\(tipDollarCent)\n" directly to the function.
I am hoping someone would be able to patiently and kindly explain to me what the best loop to use in this scenario would be, and how to properly implement it.
Here is a screenshot of my app so it is easier to see what I am trying to accomplish
If you want the textView text to append the newly created string, you can use the compound-assign operator for addition += to concatenate the what you previous had and grow it with a new string value.
var foo:String = "Foo"
let bar:String = "Bar"
foo += bar /* FooBar */ /* foo = foo + bar */
And for your comment on additions with doubles, the compound operator will also work with same-typed operands.
var pi:Double = 3.0
let fourteen:Double = 0.14
pi += fourteen /* 3.14 */ /* pi = pi + fourteen */
I am a bit confused about the code below and why the last 2 attempts to define a handler (Delegate) wont work.
//this works
let serializer_setting = new JsonSerializerSettings(Error = fun (sender:obj) (args:Serialization.ErrorEventArgs) -> ())
//this doesnt
let err_handler1 (sender:obj) (args:Serialization.ErrorEventArgs) = ()
let serializer_setting1 = new JsonSerializerSettings(Error = err_handler1)
//neither this
let err_handler2 = fun (sender:obj) (args:Serialization.ErrorEventArgs) -> ()
let serializer_setting2 = new JsonSerializerSettings(Error = err_handler2)
Aren't they exactly the same?
Edit
I also tried this
type Delegate = delegate of obj * ErrorEventArgs -> Unit
let err_handler1 (sender:obj) (args:Serialization.ErrorEventArgs) = ()
let serializer_setting1 = new JsonSerializerSettings(Error = new Delegate(err_handler1))
But this gives me the following error
Error 1 This expression was expected to have type
System.EventHandler<Serialization.ErrorEventArgs>
but here has type
Delegate
Edit 2
Taking the clue from Fyodor below if I do this
let serializer_setting1 = new JsonSerializerSettings(Error = System.EventHandler<Serialization.ErrorEventArgs>(err_handler1))
It works and this also makes sense - However I still don't understand why my approach using a Delegate wont work.
The two latter examples are F# functions, which are actually not normal .NET delegates.
For purposes of interoperability with the rest of .NET, however, the F# compiler will convert an F# function to a compatible delegate type when it can see that this is the expected type.
In the first example, Error must be a delegate, so the F# compiler can infer that it must perform the conversion.
In the two latter examples, the types of the functions are inferred by the compiler without taking into account how they're used, because the F# compiler only interprets code in a single pass from top to bottom.
When the compiler reaches the expression where the function is attempted assigned to Error, the function already has the incorrect type.
See the documentation for more information about delegates in F#.
In the following, the way I define Animator is common in OOP systems, where it is considered fine to mutate this object's state:
type MyViewController() =
inherit UIViewController()
//Is there a better way than this?
member val Animator = null with get, set
override this.ViewDidLoad() =
base.ViewDidLoad()
//this.View is defined in base class and is only valid *now*
this.Animator <- new UIDynamicAnimator(this.View)
Is there a more idiomatic way to define Animator that communicates: "null->object is ok, object -> object is forbidden"?
(Obviously, I could write a custom set function that checks and throws at runtime, but that seems more annoyingly "clever" than helpful.)
Once the library depends so heavily on inheritance, it is really hard to avoid the usual OO patterns, even when you're using F#. So to be fair, I would probably write exactly the same code you did and just live with the fact that this part of the code is not going to be that pretty (and use more functional approach in the parts of the application where you're not forced into OO style by the framework).
Also, in this case, I'd just live with the null - you can use option, but you're not getting any benefits, because you cannot do much if the value is None anyway.
Presumably, in your full source code, you are overriding other methods of the class and you are creating Animator so that it can be used in the other methods. One thing you could do is to extract the code from the class and write something like this:
type IInitializedView =
abstract OtherMethod : UIViewController -> unit
type MyViewController(viewInitialized:UIView -> IInitializedView) =
inherit UIViewController()
let mutable initializedView = None
override this.ViewDidLoad() =
base.ViewDidLoad()
initializedView := Some(viewInitialized this.View)
override this.OtherMethod() =
viewInitialized |> Option.iter (fun vi ->
vi.OtherMethod() )
The idea here is that MyViewController calls your function when it has view and your function then creates a new interface that handles the other method - but your interface is created only after everything is properly initialized!
let vc = MyViewController(fun view ->
// Now we have valid 'view' so we construct animator!
let animator = UIDynamicAnimator(view)
{ new IInitializedView with
member x.OtherMethod() =
// no problem here, because we have animator...
animator.Whatever() })
But I don't know enough about Xamarin to say whether this would work OK in a full more complex system.
I think what #Carsten gets at is that you shouldn't introduce null values unless you are absolutely forced to by interop with other CLI languages. Types declared in F# wouldn't allow them anyway, unless decorated with an AllowNullLiteralAttribute.
The value wrapped in the Option<'a> type would be a somewhat direct translation, but still has mutability. I'm assuming that you do not really need the property setter, replacing the automatic property with a let-bound value.
type MyViewController() =
inherit UIViewController()
let mutable animator = Option<UIDynamicAnimator>.None
member x.Animator = animator.Value
override this.ViewDidLoad() =
base.ViewDidLoad()
animator <- Some <| new UIDynamicAnimator(this.View)
On the other hand, wrapping in Lazy<'a> is a little bit more in the functional spirit.
type MyViewController() as this =
inherit UIViewController()
let animator = lazy(new UIDynamicAnimator(this.View))
member x.Animator = animator.Value
override this.ViewDidLoad() =
base.ViewDidLoad()
// Optional; only if you need the object creation right now
animator.Force() |> ignore
The rust by example guide shows the following code here for a fibonacci series with iterators:
fn next(&mut self) -> Option<u32> {
let new_next = self.curr + self.next;
let new_curr = mem::replace(&mut self.next, new_next);
// 'Some' is always returned, this is an infinite value generator
Some(mem::replace(&mut self.curr, new_curr))
}
I would like to understand what is the advantage of this, over the most intuitive (if you come from other languages):
fn next(&mut self) -> Option<u32> {
let tmp = self.next;
self.next = self.curr + self.next;
self.curr = tmp;
Some(self.curr)
}
It's not always possible to write the direct code, due to Rust's ownership. If self.next is storing a non-Copy type (e.g. Vec<T> for any type T) then let tmp = self.next; is taking that value out of self by-value, that is, moving ownership, so the source should not be usable. But the source is behind a reference and references must always point to valid data, so the compiler cannot allow moving out of &mut: you get errors like cannot move out of dereference of `&mut`-pointer.
replace gets around these issues via unsafe code, by internally making the guarantee that any invalidation is entirely made valid by the time replace returns.
You can see this answer for more info about moves in general, and this question for a related issue about a swap function (replace is implemented using the standard library's swap internally).
I've been trying for hours to get something as simple as displaying a line chart based on 2 dots that I supply manually and all I get is a crash. I've tried to understand how everything works based on the demo code but it's too complex. I'm not even concerned about writing nice code with onResume() etc, I just want something to display the first time I open the activity. Once I know how to do that I'll be able to adapt and learn what I need. Here's the code I came up with:
public class StatsActivity extends Activity {
private XYMultipleSeriesDataset StatsDataset = new XYMultipleSeriesDataset();
private XYMultipleSeriesRenderer StatsRenderer = new XYMultipleSeriesRenderer();
private XYSeries StatsCurrentSeries;
private GraphicalView StatsChartView;
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.stats);
LinearLayout layout = (LinearLayout) findViewById(R.id.Statschart);
StatsRenderer.setAxesColor(Color.YELLOW);
String seriesTitle = "Rank";
XYSeries series = new XYSeries(seriesTitle);
series.add(5, 7); //1st series I want to add
StatsDataset.addSeries(series);
series.add(9, 1); //the 2nd one
StatsDataset.addSeries(series);
StatsCurrentSeries = series;
System.out.println(series);
XYSeriesRenderer renderer = new XYSeriesRenderer();
renderer.setColor(Color.RED);
StatsRenderer.addSeriesRenderer(renderer);
StatsChartView = ChartFactory.getLineChartView(this, StatsDataset,StatsRenderer);
layout.addView(StatsChartView);
}
}
I've been reading the docs to determine what each function does but in the end I still can't get anything to display.
Thanks!
The big thing that I struggled with is that you need a renderer for each XYSeries. You have two series here, but just one renderer - I just create/add renderers when I input data. Also, Android is mostly pass-by-reference, so you've passed the same data set in twice (i.e. your second update to the data will be mirrored "in" the MultipleSeriesDataset).